diff options
author | Heinrich Wendel <lanius@gentoo.org> | 2003-08-15 13:10:47 +0000 |
---|---|---|
committer | Heinrich Wendel <lanius@gentoo.org> | 2003-08-15 13:10:47 +0000 |
commit | 4cd8b6ff5b9552adb4153d0efa2036da6573978c (patch) | |
tree | 95f34e04ae38fe9cb311b2c2ab20bfdcc1b6ffca /app-portage | |
parent | fixed portagemaster path (diff) | |
download | gentoo-2-4cd8b6ff5b9552adb4153d0efa2036da6573978c.tar.gz gentoo-2-4cd8b6ff5b9552adb4153d0efa2036da6573978c.tar.bz2 gentoo-2-4cd8b6ff5b9552adb4153d0efa2036da6573978c.zip |
moved rmerge2 from app-admin to app-portage
Diffstat (limited to 'app-portage')
-rw-r--r-- | app-portage/rmerge2/ChangeLog | 24 | ||||
-rw-r--r-- | app-portage/rmerge2/Manifest | 7 | ||||
-rw-r--r-- | app-portage/rmerge2/files/digest-rmerge2-0.9.7 | 0 | ||||
-rw-r--r-- | app-portage/rmerge2/files/digest-rmerge2-0.9.9 | 0 | ||||
-rw-r--r-- | app-portage/rmerge2/files/rmerge2-0.9.7 | 476 | ||||
-rw-r--r-- | app-portage/rmerge2/files/rmerge2-0.9.9 | 467 | ||||
-rw-r--r-- | app-portage/rmerge2/rmerge2-0.9.7.ebuild | 19 | ||||
-rw-r--r-- | app-portage/rmerge2/rmerge2-0.9.9.ebuild | 19 |
8 files changed, 1012 insertions, 0 deletions
diff --git a/app-portage/rmerge2/ChangeLog b/app-portage/rmerge2/ChangeLog new file mode 100644 index 000000000000..d7707d4359f7 --- /dev/null +++ b/app-portage/rmerge2/ChangeLog @@ -0,0 +1,24 @@ +# ChangeLog for app-admin/rmerge2 +# Copyright 2002-2003 Gentoo Technologies, Inc.; Distributed under the GPL v2 +# $Header: /var/cvsroot/gentoo-x86/app-portage/rmerge2/ChangeLog,v 1.1 2003/08/15 13:10:41 lanius Exp $ + +*rmerge2-0.9.7 (15 Aug 2003) + + 15 Aug 2003; Heinrich Wendel <lanius@gentoo.org> rmerge2-0.9.7.ebuild, + rmerge2-0.9.9.ebuild, files/rmerge2-0.9.7, files/rmerge2-0.9.9: + moved from app-admin to app-portage + +*rmerge2-0.9.9 (07 May 2003) + + 07 May 2003; Martin Holzer <mholzer@gentoo.org> rmerge2-0.9.9.ebuild, + files/rmerge2-0.9.9: + Version bumped. Submitted by Bardur Arantsson + <bardur-gta@odense.kollegienet.dk> in #11495. + + 06 Dec 2002; Rodney Rees <manson@gentoo.org> : changed sparc ~sparc keywords + +*rmerge2-0.9.7 (31 Oct 2002) + + 31 Oct 2002; Karl Trygve Kalleberg <karltk@gentoo.org> rmerge2-0.9.7.ebuild files/digest-rmerge2-0.9.7 files/rmerge2-0.9.7 : + Initial import. Ebuild submitted by Bardur Arantsson + <bardur-gta@odense.kollegienet.dk>. diff --git a/app-portage/rmerge2/Manifest b/app-portage/rmerge2/Manifest new file mode 100644 index 000000000000..8a3b8cd388b8 --- /dev/null +++ b/app-portage/rmerge2/Manifest @@ -0,0 +1,7 @@ +MD5 06be1429502f88399135c53886eb443b rmerge2-0.9.7.ebuild 557 +MD5 829d146490ad3aecabcd33693445e6c5 ChangeLog 980 +MD5 91b65c5d2fd278bde3b7b81527c2f29a rmerge2-0.9.9.ebuild 557 +MD5 d41d8cd98f00b204e9800998ecf8427e files/digest-rmerge2-0.9.7 0 +MD5 d41d8cd98f00b204e9800998ecf8427e files/digest-rmerge2-0.9.9 0 +MD5 d763c3cdb217883521b611b3d37d08c2 files/rmerge2-0.9.7 16399 +MD5 b2aa1ecc9fdf2394e02511d7355471b7 files/rmerge2-0.9.9 16102 diff --git a/app-portage/rmerge2/files/digest-rmerge2-0.9.7 b/app-portage/rmerge2/files/digest-rmerge2-0.9.7 new file mode 100644 index 000000000000..e69de29bb2d1 --- /dev/null +++ b/app-portage/rmerge2/files/digest-rmerge2-0.9.7 diff --git a/app-portage/rmerge2/files/digest-rmerge2-0.9.9 b/app-portage/rmerge2/files/digest-rmerge2-0.9.9 new file mode 100644 index 000000000000..e69de29bb2d1 --- /dev/null +++ b/app-portage/rmerge2/files/digest-rmerge2-0.9.9 diff --git a/app-portage/rmerge2/files/rmerge2-0.9.7 b/app-portage/rmerge2/files/rmerge2-0.9.7 new file mode 100644 index 000000000000..b9c4fc933ac2 --- /dev/null +++ b/app-portage/rmerge2/files/rmerge2-0.9.7 @@ -0,0 +1,476 @@ +#!/usr/bin/python +# +# rmerge2 is a robust version of "emerge -e" which supports +# resumption of builds. It was inspired by the original rmerge +# bash script by Dan Doel <dolio@zoomtown.com> which did basically +# the same thing (but not quite). +# +# Copyright (C) 2002 Bardur Arantsson <bardur@imada.sdu.dk> +# +# 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. +# +# See http://www.gnu.org/copyleft/gpl.html for further information. +# + +VERSION = "0.9.7" + +# system configuration (there should be no need to change these) +STATEDIR="/var/lib/rmerge2" +EMERGE="/usr/bin/emerge" + +# default option values (DO NOT CHANGE UNLESS YOU KNOW WHAT YOU ARE DOING) +opt_empty = 0 +opt_pretend = 0 +opt_skip = 0 +opt_resume = 1 +opt_force = 0 +opt_quiet = 0 +opt_statefile = None + +# This is an option which is purely for testing. +# It causes merge_pkg to not do anything dangerous, +# and in addition randomly fail emerge runs. Useful +# for testing if resumption of builds works properly. +opt_test = 0 + +# +# Start of the actual code +# +import getopt +import os +import md5 +import re +import whrandom +from popen2 import * +import cPickle +pickle = cPickle +import portage + +# read portage configuration to avoid relying on +# the environment +portage_config = portage.config() + +# fatal error +def fatal(message): + os.sys.stderr.write("rmerge2: %s\n" % (message,)) + os.sys.stderr.flush() + os.sys.exit(1) + +class MergeError(Exception): + pass + +class State: + """The state object represents the state of the + compilation. It is pickled/unpickled to preserve + state information across invocations.""" + + def __init__(self, todo, _failed = [], skipped = []): + """Create an initially empty state.""" + self.todo = todo + self._failed = _failed + self.skipped = skipped + + def skip(self, n): + """Skip n packages.""" + self.skipped.extend(self.todo[:n]) + self.todo = self.todo[n:] + + def any_left(self): + """Are there any packages left to do?""" + return len(self.todo) != 0 + + def get_next(self): + """Return the next package from the to do list.""" + return self.todo[0] + + def get_failed(self): + """Return the list of failed packages.""" + return self._failed + + def get_todo(self): + """Return the list of packages left to do.""" + return self.todo + + def done(self): + """Mark the first package as done.""" + del self.todo[0] + + def failed(self): + """The current package failed (completely).""" + self._failed.append(self.todo[0]) + del self.todo[0] + + def dump(self, fname): + """Write state to file named fname in a crashproof way.""" + f = open("%s.tmp" % fname,"w") + try: + pickle.dump(self, f) + os.rename("%s.tmp" % fname, fname) + except: + os.unlink("%s.tmp" % fname) # don't need anymore + raise + +def load_state(fname): + return pickle.load(open(fname,"r")) + +def split_pkgname(pkgname): + """Chops off the version number and returns a tuple + of (basename, version) for the given package name.""" + m = re.search("(.*?)(-[.0-9a-zA-Z]+(-r[0-9]+)?)$",pkgname) + if m: + pkg_ver = m.group(2)[1:] + pkgname = m.group(1) + else: + pkg_ver = None + + return (pkgname,pkg_ver) + +def get_state_filename(pkgnames, create=1): + """Compute the status directory name for the + given list of packages.""" + if opt_statefile: + return opt_statefile + else: + # Generate a state file name if none is given + pkgnames = pkgnames[:] # sorting is destructive + pkgnames.sort() # we want to ignore ordering + + # hash each of the package names + h = md5.new() + for pkgname in pkgnames: + h.update(pkgname.strip()) + + return os.path.join(STATEDIR,h.hexdigest()) + +def get_prereqs(pkgnames): + """Compute prerequisite packages for the given packages and + return the list. The list contains full package names (category, + package name and version number). If opt_empty is NOT set, + only packages which were originally listed are returned. However, + this is still useful for determining the proper ordering of those + packages.""" + if not opt_empty: + # build a dictionary of package names for quick lookup. + # this is used to determine if we should merge a given + # package. this is a very crude way of doing this, but + # portage doesn't allow us to find out (inexpensively) + # whether a given package is part of another which is + # really what we need if pkgnames contains virtual + # packages. Oh, well. + pkgnames_dict = {} + for pkgname in pkgnames: + pkgnames_dict[pkgname] = None # value doesn't matter + + # If any input packages contain version numbers, we should + # repesct them. Do this by adding a '=' in front so that + # emerge knows that we want a specific version + qpkgnames = pkgnames[:] # copy to avoid overwriting + for i in xrange(0,len(pkgnames)): + pkgname = pkgnames[i] + if split_pkgname(pkgname)[1]!=None: # fixed version? + qpkgnames[i] = ("=%s" % pkgname) + + # Evil hack to retrieve list of package names (and versions) + r = re.compile("\\[ebuild......\\]\\s+([^\\s]+)") + + # Use emerge to get the canonical package list. + # NOTE: If "-e" is not present, emerge will not + # compute interdependencies between packages properly + emerge = Popen3([EMERGE,"-p","-e"] + qpkgnames) + prereqs = [] # current list of prerequisite packages + lines = emerge.fromchild.readlines() + for line in lines: + m = r.match(line) + if m: + if not opt_empty: # only want requested packages to be merged? + # full package name(s) + fpkgname = m.group(1) + fpkgname_nocat = fpkgname.split("/")[1] # without category + # get base name of package + pkgname = split_pkgname(fpkgname)[0] + pkgname_nocat = pkgname.split("/")[1] # without category + if not fpkgname in pkgnames_dict and \ + not fpkgname_nocat in pkgnames_dict and \ + not pkgname in pkgnames_dict and \ + not pkgname_nocat in pkgnames_dict: + continue # move on to next package + # append package to list of prereqs + prereqs.append(m.group(1)) + + # status? + status = emerge.wait() + if not (os.WIFEXITED(status) and os.WEXITSTATUS(status)==0): + os.sys.stderr.write("emerge output:\n\n") + os.sys.stderr.writelines(lines) + fatal("emerge returned bad exit status. cannot continue.") + + return prereqs + +def merge_pkg(pkgname): + """This function merges a single package. The package name + must be given as a full package name, i.e. with a category + and version number.""" + if opt_test: + if whrandom.choice([0,1,1]): # "fail" random packages + pass # fake successful merge + else: + raise MergeError("Fake merge error") + else: + print "Merging %s..." % pkgname + os.sys.stdout.flush() + # This is a bit more complicated than using Popen, + # but we really don't have any choice if we want + # to "forward" the output + pid = os.fork() + if pid==0: # child? + if opt_quiet: + try: # pipe to the great void + devnull = open("/dev/null","w") + os.dup2(devnull.fileno(),0) + os.dup2(devnull.fileno(),1) + devnull.close() + except os.error,e: + pass # not that important + os.execv(EMERGE,[EMERGE,"--oneshot","=%s" % pkgname]) + fatal("execv() failed: cannot continue") + + # wait for emerge to finish + try: + status = os.waitpid(pid,0)[1] + except os.error,e: + status = 0 # child finished already? + # abnormal exit? + if not (os.WIFEXITED(status) and os.WEXITSTATUS(status)==0): + raise MergeError("Failed to merge %s..." % pkgname) + +def merge_pkgs(pkgnames): + """Merge given packages using emerge. The input list of package names + contains package names *without* version numbers. This function + maintains state between each merge to avoid having to remerge + previously merged packages.""" + + # state file + state_filename = get_state_filename(pkgnames) + + # start environment (note that the current environment + # overrides Portage variables as usual). + start_env = { "CFLAGS" : os.environ.get("CFLAGS", + portage_config["CFLAGS"]), + "CXXFLAGS" : os.environ.get("CXXFLAGS", + portage_config["CXXFLAGS"]) } + + # environment to use when a package fails to merge + BCFLAGS = os.environ.get("BCFLAGS",None) + if BCFLAGS==None and portage_config.has_key("BCFLAGS"): + BCFLAGS=portage_config["BCFLAGS"] + + BCXXFLAGS = os.environ.get("BCXXFLAGS",None) + if BCXXFLAGS==None and portage_config.has_key("BCXXFLAGS"): + BCXXFLAGS=portage_config["BCXXFLAGS"] + + # backup environment to use when emerge fails + if BCFLAGS==None and BCXXFLAGS==None: + backup_env = None + elif BCFLAGS!=None and BCXXFLAGS!=None: + backup_env = { "CFLAGS": BCFLAGS, "CXXFLAGS": BCXXFLAGS } + else: + raise MergeError("Must either set both BCFLAGS and BCXXFLAGS or neither.") + + # if we want to continue and a saved state exists + if opt_resume and os.path.exists(state_filename): + state = load_state(state_filename) + else: + state = State(get_prereqs(pkgnames)) + + # skip opt_skip packages + state.skip(opt_skip) + + # if we're just pretending, display + # a list of packages that would be merged + if opt_pretend: + print "I would merge the following packages (in order):\n\n" + print "\n".join(state.get_todo()) + else: + # merge each package in turn + while state.any_left(): + # get the name of the first package left to do + pkgname = state.get_next() + tries=0 # how many times have we tried this package? + while (tries <= 1): + try: + # merge and restore original flags + try: + merge_pkg(pkgname) + finally: + os.environ.update(start_env) + # update the list of packages left to do. + state.done() + break # stop trying this package + except MergeError,e: + # Don't retry unless there is a backup_env + if (tries == 0) and backup_env: + # Let's retry with different flags + tries += 1 + os.environ.update(backup_env) + else: + if opt_force: + state.failed() # register failure and continue + tries += 1 + else: + raise # give up + # dump state each time through the loop + state.dump(state_filename) + + # list the packages that failed to merge + if len(state.get_failed())>0: + print + print "The following packages failed to merge:" + print + print "\n".join(state.get_failed()) + +# +# Main program +# +try: + # parse options + opts,args = getopt.getopt(os.sys.argv[1:],"epsfqV", + ["emptytree","pretend","skip=","scratch", + "force","quiet","help","statefile=","version"]) + for (opt,val) in opts: + if opt in ["-e","--emptytree"]: + opt_empty = 1 + elif opt in ["-p","--pretend"]: + opt_pretend = 1 + elif opt in ["--skip"]: + opt_skip = int(val) + elif opt in ["-s","--scratch"]: + opt_resume = 0 + elif opt in ["-f","--force"]: + opt_force = 1 + elif opt in ["-q","--quiet"]: + opt_quiet = 1 + elif opt in ["-h","--help"]: + raise getopt.error("",None) + elif opt in ["-V","--version"]: + print "rmerge2 %s" % (VERSION,) + os.sys.exit(0) + elif opt in ["--statefile"]: + opt_statefile = val + + # require explicit package names + if len(args)==0: + raise getopt.error("Please specify a package name.","") + + # check that CFLAGS and CXXFLAGS are set + if not os.environ.has_key("CFLAGS"): + fatal("must set CFLAGS in /etc/make.conf or environment") + if not os.environ.has_key("CXXFLAGS"): + fatal("must set CXXFLAGS in /etc/make.conf or environment") + + # anything other than a "pretend" run requires + # root privileges. + if (not opt_pretend) and (os.getuid() != 0): + fatal("root privileges required") + + # merge packages + merge_pkgs(args) + +except getopt.error,e: + # any reason for exception or do we just want + # to display usage? + if e: + print "rmerge2: %s" % (e,) + print + # print the standard usage + print """Usage: + + rmerge2 [options] package1 ... + +Synopsis: + + Robustly merges all the given packages using emerge. + +Options: + +-h, --help + Prints this message. + +-e, --emptytree + Pretend that no packages (besides glibc) are installed. + This will cause all the packages the given package(s) + depend on to be remerged. Useful for rebuilding the + entire distribution from scratch. + +-p, --pretend + Do not merge any packages, just display which packages + would be merged. + +--skip=N + Skip the given number of packages. Useful if you are + doing a manual merge of a lot of packages and just want + to skip a package that is giving you trouble. + +-s, --scratch + Start a new merge of the given packages from scratch. + Forces rmerge2 to ignore any previous progress information. + Useful if you want to be absolutely certain that + ALL packages are remerged, even after having merged some + of them previously. NOTE: This only applies to the given + set of packages, you could (in theory at least) have + other package batches which could be continued independently + of this run. + +-f, --force + Keep running even after packages fail to merge. The + list of packages which failed to merge is displayed + at the end of a complete run. Very useful for a + completely automated rebuild. + + NOTE: If a package fails to merge with the normal + CFLAGS/CXXFLAGS the rmerge2 script will retry merging + using BCFLAGS/BCXXFLAGS from your environment or porage + configuration files. + +-q, --quiet + Do not display output from emerge runs. This makes the + run almost completely noise-free, but errors from portage + will NOT be displayed. Also, watching these compiles can + be VERY tedious if the individual packages take a long time + to merge. + +--statefile + Save the program state in this file instead of the standard + location (which is determined from package names and versions + given on the command line). + +-V, --version + Show program version. + +Examples: + + Remerge all installed packages (except glibc): + + rmerge2 -f -s -e world + + Remerge given packages in the appropriate order + (as determined by their dependency lists): + + rmerge2 package1 package2 ... + + Note that this does not work properly on "virtual" + packages (e.g. "kde") because of the way portage + currently works. + + Remerge given package(s) and all the packages it + depend on: + + rmerge2 -e package1 [package2 ...] +""" diff --git a/app-portage/rmerge2/files/rmerge2-0.9.9 b/app-portage/rmerge2/files/rmerge2-0.9.9 new file mode 100644 index 000000000000..28a2e1a19875 --- /dev/null +++ b/app-portage/rmerge2/files/rmerge2-0.9.9 @@ -0,0 +1,467 @@ +#!/usr/bin/python +# +# rmerge2 is a robust version of "emerge -e" which supports +# resumption of builds. It was inspired by the original rmerge +# bash script by Dan Doel <dolio@zoomtown.com> which did basically +# the same thing (but not quite). +# +# Copyright (C) 2002 Bardur Arantsson <bardur@imada.sdu.dk> +# +# 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. +# +# See http://www.gnu.org/copyleft/gpl.html for further information. +# + +VERSION = "0.9.9" + +# system configuration (there should be no need to change these) +STATEDIR="/var/lib/rmerge2" +EMERGE="/usr/bin/emerge" + +# default option values (DO NOT CHANGE UNLESS YOU KNOW WHAT YOU ARE DOING) +opt_empty = 0 +opt_pretend = 0 +opt_skip = 0 +opt_resume = 1 +opt_force = 0 +opt_quiet = 0 +opt_statefile = None + +# This is an option which is purely for testing. +# It causes merge_pkg to not do anything dangerous, +# and in addition randomly fail emerge runs. Useful +# for testing if resumption of builds works properly. +opt_test = 0 + +# +# Start of the actual code +# +import getopt +import os +import md5 +import re +import whrandom +from popen2 import * +import cPickle +pickle = cPickle +import portage + +# read portage configuration to avoid relying on +# the environment +portage_config = portage.config() + +# fatal error +def fatal(message): + os.sys.stderr.write("rmerge2: %s\n" % (message,)) + os.sys.stderr.flush() + os.sys.exit(1) + +class MergeError(Exception): + pass + +class State: + """The state object represents the state of the + compilation. It is pickled/unpickled to preserve + state information across invocations.""" + + def __init__(self, todo, _failed = [], skipped = []): + """Create an initially empty state.""" + self.todo = todo + self._failed = _failed + self.skipped = skipped + + def skip(self, n): + """Skip n packages.""" + self.skipped.extend(self.todo[:n]) + self.todo = self.todo[n:] + + def any_left(self): + """Are there any packages left to do?""" + return len(self.todo) != 0 + + def get_next(self): + """Return the next package from the to do list.""" + return self.todo[0] + + def get_failed(self): + """Return the list of failed packages.""" + return self._failed + + def get_todo(self): + """Return the list of packages left to do.""" + return self.todo + + def done(self): + """Mark the first package as done.""" + del self.todo[0] + + def failed(self): + """The current package failed (completely).""" + self._failed.append(self.todo[0]) + del self.todo[0] + + def dump(self, fname): + """Write state to file named fname in a crashproof way.""" + f = open("%s.tmp" % fname,"w") + try: + pickle.dump(self, f) + os.rename("%s.tmp" % fname, fname) + except: + os.unlink("%s.tmp" % fname) # don't need anymore + raise + +def load_state(fname): + return pickle.load(open(fname,"r")) + +def split_pkgname(pkgname): + """Chops off the version number and returns a tuple + of (basename, version) for the given package name.""" + x = portage.pkgsplit(pkgname) + if not x: + return (pkgname,None) + elif x[2]=="r0": + return (x[0], x[1]) + else: + return (x[0],"-".join(x[1:3])) + +def get_state_filename(pkgnames, create=1): + """Compute the status directory name for the + given list of packages.""" + if opt_statefile: + return opt_statefile + else: + # Generate a state file name if none is given + pkgnames = pkgnames[:] # sorting is destructive + pkgnames.sort() # we want to ignore ordering + + # hash each of the package names + h = md5.new() + for pkgname in pkgnames: + h.update(pkgname.strip()) + + return os.path.join(STATEDIR,h.hexdigest()) + +def get_prereqs(pkgnames): + """Compute prerequisite packages for the given packages and + return the list. The list contains full package names (category, + package name and version number). If opt_empty is NOT set, + only packages which were originally listed are returned. However, + this is still useful for determining the proper ordering of those + packages.""" + if not opt_empty: + # build a dictionary of package names for quick lookup. + # this is used to determine if we should merge a given + # package. this is a very crude way of doing this, but + # portage doesn't allow us to find out (inexpensively) + # whether a given package is part of another which is + # really what we need if pkgnames contains virtual + # packages. Oh, well. + pkgnames_dict = {} + for pkgname in pkgnames: + pkgnames_dict[pkgname] = None # value doesn't matter + + # If any input packages contain version numbers, we should + # repesct them. Do this by adding a '=' in front so that + # emerge knows that we want a specific version + qpkgnames = pkgnames[:] # copy to avoid overwriting + for i in xrange(0,len(pkgnames)): + pkgname = pkgnames[i] + if split_pkgname(pkgname)[1]!=None: # fixed version? + qpkgnames[i] = ("=%s" % pkgname) + + # Evil hack to retrieve list of package names (and versions) + r = re.compile("\\[ebuild......\\]\\s+([^\\s]+)") + + # Use emerge to get the canonical package list. + # NOTE: If "-e" is not present, emerge will not + # compute interdependencies between packages properly + emerge = Popen3([EMERGE,"-p","-e"] + qpkgnames) + prereqs = [] # current list of prerequisite packages + lines = emerge.fromchild.readlines() + for line in lines: + m = r.match(line) + if m: + if not opt_empty: # only want requested packages to be merged? + # full package name(s) + fpkgname = m.group(1) + fpkgname_nocat = fpkgname.split("/")[1] # without category + # get base name of package + pkgname = split_pkgname(fpkgname)[0] + pkgname_nocat = pkgname.split("/")[1] # without category + if not fpkgname in pkgnames_dict and \ + not fpkgname_nocat in pkgnames_dict and \ + not pkgname in pkgnames_dict and \ + not pkgname_nocat in pkgnames_dict: + continue # move on to next package + # append package to list of prereqs + prereqs.append(m.group(1)) + + # status? + status = emerge.wait() + if not (os.WIFEXITED(status) and os.WEXITSTATUS(status)==0): + os.sys.stderr.write("emerge output:\n\n") + os.sys.stderr.writelines(lines) + fatal("emerge returned bad exit status. cannot continue.") + + return prereqs + +def merge_pkg(pkgname): + """This function merges a single package. The package name + must be given as a full package name, i.e. with a category + and version number.""" + if opt_test: + if whrandom.choice([0,1,1]): # "fail" random packages + pass # fake successful merge + else: + raise MergeError("Fake merge error") + else: + print "Merging %s..." % pkgname + os.sys.stdout.flush() + # This is a bit more complicated than using Popen, + # but we really don't have any choice if we want + # to "forward" the output + pid = os.fork() + if pid==0: # child? + if opt_quiet: + try: # pipe to the great void + devnull = open("/dev/null","w") + os.dup2(devnull.fileno(),0) + os.dup2(devnull.fileno(),1) + devnull.close() + except os.error,e: + pass # not that important + os.execv(EMERGE,[EMERGE,"--oneshot","=%s" % pkgname]) + fatal("execv() failed: cannot continue") + + # wait for emerge to finish + try: + status = os.waitpid(pid,0)[1] + except os.error,e: + status = 0 # child finished already? + # abnormal exit? + if not (os.WIFEXITED(status) and os.WEXITSTATUS(status)==0): + raise MergeError("Failed to merge %s..." % pkgname) + +def merge_pkgs(pkgnames): + """Merge given packages using emerge. The input list of package names + contains package names *without* version numbers. This function + maintains state between each merge to avoid having to remerge + previously merged packages.""" + + # state file + state_filename = get_state_filename(pkgnames) + + # start environment (note that the current environment + # overrides Portage variables as usual). + start_env = { "CFLAGS" : os.environ.get("CFLAGS", + portage_config["CFLAGS"]), + "CXXFLAGS" : os.environ.get("CXXFLAGS", + portage_config["CXXFLAGS"]) } + + # environment to use when a package fails to merge + BCFLAGS = os.environ.get("BCFLAGS",None) + if BCFLAGS==None and portage_config.has_key("BCFLAGS"): + BCFLAGS=portage_config["BCFLAGS"] + + BCXXFLAGS = os.environ.get("BCXXFLAGS",None) + if BCXXFLAGS==None and portage_config.has_key("BCXXFLAGS"): + BCXXFLAGS=portage_config["BCXXFLAGS"] + + # backup environment to use when emerge fails + if BCFLAGS==None and BCXXFLAGS==None: + backup_env = None + elif BCFLAGS!=None and BCXXFLAGS!=None: + backup_env = { "CFLAGS": BCFLAGS, "CXXFLAGS": BCXXFLAGS } + else: + raise MergeError("Must either set both BCFLAGS and BCXXFLAGS or neither.") + + # if we want to continue and a saved state exists + if opt_resume and os.path.exists(state_filename): + state = load_state(state_filename) + elif len(pkgnames)==0: + raise getopt.error("Please specify at least one package name.","") + else: + state = State(get_prereqs(pkgnames)) + + # skip opt_skip packages + state.skip(opt_skip) + + # if we're just pretending, display + # a list of packages that would be merged + if opt_pretend: + print "I would merge the following packages (in order):\n\n" + print "\n".join(state.get_todo()) + else: + # merge each package in turn + while state.any_left(): + # get the name of the first package left to do + pkgname = state.get_next() + tries=0 # how many times have we tried this package? + while (tries <= 1): + try: + # merge and restore original flags + try: + merge_pkg(pkgname) + finally: + os.environ.update(start_env) + # update the list of packages left to do. + state.done() + break # stop trying this package + except MergeError,e: + # Don't retry unless there is a backup_env + if (tries == 0) and backup_env: + # Let's retry with different flags + tries += 1 + os.environ.update(backup_env) + else: + if opt_force: + state.failed() # register failure and continue + tries += 1 + else: + raise # give up + # dump state each time through the loop + state.dump(state_filename) + + # list the packages that failed to merge + if len(state.get_failed())>0: + print + print "The following packages failed to merge:" + print + print "\n".join(state.get_failed()) + +# +# Main program +# +try: + # parse options + opts,args = getopt.getopt(os.sys.argv[1:],"epsfqV", + ["emptytree","pretend","skip=","scratch", + "force","quiet","help","statefile=","version"]) + for (opt,val) in opts: + if opt in ["-e","--emptytree"]: + opt_empty = 1 + elif opt in ["-p","--pretend"]: + opt_pretend = 1 + elif opt in ["--skip"]: + opt_skip = int(val) + elif opt in ["-s","--scratch"]: + opt_resume = 0 + elif opt in ["-f","--force"]: + opt_force = 1 + elif opt in ["-q","--quiet"]: + opt_quiet = 1 + elif opt in ["-h","--help"]: + raise getopt.error("",None) + elif opt in ["-V","--version"]: + print "rmerge2 %s" % (VERSION,) + os.sys.exit(0) + elif opt in ["--statefile"]: + opt_statefile = val + + # anything other than a "pretend" run requires + # root privileges. + if (not opt_pretend) and (os.getuid() != 0): + fatal("root privileges required") + + # merge packages + merge_pkgs(args) + +except getopt.error,e: + # any reason for exception or do we just want + # to display usage? + if e: + print "rmerge2: %s" % (e,) + print + # print the standard usage + print """Usage: + + rmerge2 [options] package1 ... + +Synopsis: + + Robustly merges all the given packages using emerge. + +Options: + +-h, --help + Prints this message. + +-e, --emptytree + Pretend that no packages (besides glibc) are installed. + This will cause all the packages the given package(s) + depend on to be remerged. Useful for rebuilding the + entire distribution from scratch. + +-p, --pretend + Do not merge any packages, just display which packages + would be merged. + +--skip=N + Skip the given number of packages. Useful if you are + doing a manual merge of a lot of packages and just want + to skip a package that is giving you trouble. + +-s, --scratch + Start a new merge of the given packages from scratch. + Forces rmerge2 to ignore any previous progress information. + Useful if you want to be absolutely certain that + ALL packages are remerged, even after having merged some + of them previously. NOTE: This only applies to the given + set of packages, you could (in theory at least) have + other package batches which could be continued independently + of this run. + +-f, --force + Keep running even after packages fail to merge. The + list of packages which failed to merge is displayed + at the end of a complete run. Very useful for a + completely automated rebuild. + + NOTE: If a package fails to merge with the normal + CFLAGS/CXXFLAGS the rmerge2 script will retry merging + using BCFLAGS/BCXXFLAGS from your environment or portage + configuration files. + +-q, --quiet + Do not display output from emerge runs. This makes the + run almost completely noise-free, but errors from portage + will NOT be displayed. Also, watching these compiles can + be VERY tedious if the individual packages take a long time + to merge. + +--statefile=FILENAME + Save the program state in this file instead of the standard + location (which is determined from package names and versions + given on the command line). + +-V, --version + Show program version. + +Examples: + + Remerge all installed packages (except glibc): + + rmerge2 -f -s -e world + + Remerge given packages in the appropriate order + (as determined by their dependency lists): + + rmerge2 package1 package2 ... + + Note that this does not work properly on "virtual" + packages (e.g. "kde") because of the way portage + currently works. + + Remerge given package(s) and all the packages it + depend on: + + rmerge2 -e package1 [package2 ...] +""" diff --git a/app-portage/rmerge2/rmerge2-0.9.7.ebuild b/app-portage/rmerge2/rmerge2-0.9.7.ebuild new file mode 100644 index 000000000000..0283b6413b7f --- /dev/null +++ b/app-portage/rmerge2/rmerge2-0.9.7.ebuild @@ -0,0 +1,19 @@ +# Copyright 1999-2003 Gentoo Technologies, Inc. +# Distributed under the terms of the GNU General Public License v2 +# $Header: /var/cvsroot/gentoo-x86/app-portage/rmerge2/rmerge2-0.9.7.ebuild,v 1.1 2003/08/15 13:10:41 lanius Exp $ + +DESCRIPTION="robust version of 'emerge --emptytree' which supports resumption/forcing of builds" +HOMEPAGE="http://www.gentoo.org/" + +LICENSE="GPL-2" +SLOT="0" +KEYWORDS="x86 ~ppc ~sparc ~alpha" + +DEPEND="sys-apps/portage" + +src_install() { + insinto /usr/bin + insopts -m755 + newins ${FILESDIR}/${PF} rmerge2 + dodir /var/lib/rmerge2 +} diff --git a/app-portage/rmerge2/rmerge2-0.9.9.ebuild b/app-portage/rmerge2/rmerge2-0.9.9.ebuild new file mode 100644 index 000000000000..8b325123658e --- /dev/null +++ b/app-portage/rmerge2/rmerge2-0.9.9.ebuild @@ -0,0 +1,19 @@ +# Copyright 1999-2003 Gentoo Technologies, Inc. +# Distributed under the terms of the GNU General Public License v2 +# $Header: /var/cvsroot/gentoo-x86/app-portage/rmerge2/rmerge2-0.9.9.ebuild,v 1.1 2003/08/15 13:10:41 lanius Exp $ + +DESCRIPTION="robust version of 'emerge --emptytree' which supports resumption/forcing of builds" +HOMEPAGE="http://www.gentoo.org/" + +LICENSE="GPL-2" +SLOT="0" +KEYWORDS="x86 ~ppc ~sparc ~alpha" + +DEPEND="sys-apps/portage" + +src_install() { + insinto /usr/bin + insopts -m755 + newins ${FILESDIR}/${PF} rmerge2 + dodir /var/lib/rmerge2 +} |