From: Fabian Groffen Date: Sat, 21 Jul 2007 19:19:50 +0000 (-0000) Subject: Merged from trunk 7307:7340 X-Git-Url: http://git.tremily.us/gitweb.cgi?a=commitdiff_plain;h=9a025b84e13814ea116ba56b563865a9849f6c75;p=portage.git Merged from trunk 7307:7340 +------------------------------------------------------------------------+ | 7308 | With --prune and --depclean, if --verbose is enabled then | | zmedico | show reverse dependencies for each package that is matched | | | by an atom from the command line. This allows the user to | | | see why a package will not be unmerged. | |---------+--------------------------------------------------------------| | 7309 | Make --prune bail out like depclean does if there are | | zmedico | unresolved dependencies. Notify the user that they can use | | | --nodeps in that case. | |---------+--------------------------------------------------------------| | 7310 | Make --prune try to prune all slotted packages if no atoms | | zmedico | are given (that's how it already works with --nodeps). | |---------+--------------------------------------------------------------| | 7312 | For bug #151653, make --prune keep the highest version | | zmedico | whenever possible (even when there is one with a higher | | | counter installed). | |---------+--------------------------------------------------------------| | 7313 | Fix --prune so that it always correctly accounts for COUNTER | | zmedico | when dealing with packages in the same slot. | |---------+--------------------------------------------------------------| | 7315 | Update --prune docs for bug #151653. | | zmedico | | |---------+--------------------------------------------------------------| | 7317 | Document --prune and --depclean enhancements. | | zmedico | | |---------+--------------------------------------------------------------| | 7319 | For bug #185504, document profiles/repo_name. | | zmedico | | |---------+--------------------------------------------------------------| | 7321 | If the fetcher reported success and the file is too small, | | zmedico | don't attempt to resume. Upstream probably changed the | | | distfile and we need to show a digest verification failure | | | so the user gets a clue. | |---------+--------------------------------------------------------------| | 7325 | When digest verification of a distfile fails, rename instead | | zmedico | of unlinking it in case it's a large download and the user | | | wants to salvage it due to a bad digest. | |---------+--------------------------------------------------------------| | 7327 | Hint about --nodeps if --prune doesn't find anything to | | zmedico | prune. Also enable reverse dependency display for global | | | depclean. | |---------+--------------------------------------------------------------| | 7329 | Optimize PackageSet._updateAtomMap() for the case where one | | zmedico | or more atoms are added and none are removed. | |---------+--------------------------------------------------------------| | 7330 | For bug #185930, direct all FETCHCOMMAND output to stdout. | | zmedico | | |---------+--------------------------------------------------------------| | 7332 | Just show [0] instead of [0=>0] in the repo display. | | zmedico | | |---------+--------------------------------------------------------------| | 7334 | Prune really uses all installed instead of world. It's not a | | zmedico | real reverse dependency so don't display it as such. | |---------+--------------------------------------------------------------| | 7336 | Make --prune and --depclean return before calculating | | zmedico | dependencies if atoms are given but they don't match any | | | packages. | |---------+--------------------------------------------------------------| | 7338 | For bug #185153, in commit mode do the "Fetching trivial | | zmedico | updates" routine before the qa scan. | |---------+--------------------------------------------------------------| | 7340 | For bug #169500, detect when the cvs server puts /Attic/ | | zmedico | inside the $Header path and automatically correct it. | +------------------------------------------------------------------------+ svn path=/main/branches/prefix/; revision=7342 --- diff --git a/RELEASE-NOTES b/RELEASE-NOTES index 030ebfd1f..f6989a14d 100644 --- a/RELEASE-NOTES +++ b/RELEASE-NOTES @@ -37,6 +37,12 @@ portage-2.1.3 world file and prevent matching packages from being removed. A slot atom will be recorded in the world file for any atom that is precise enough to identify a specific slot. +* For safer operation, emerge --prune will not unmerge packages that have + reverse dependencies. Use --verbose to display reverse dependencies. Use + --nodeps to completely ignore dependencies. +* emerge --depclean now accepts atoms and will unmerge only the specified + packages if nothing depends on them. Use --verbose to display reverse + dependencies. portage-2.1.2 ================================== diff --git a/bin/repoman b/bin/repoman index 6809bc980..4f00f95d0 100755 --- a/bin/repoman +++ b/bin/repoman @@ -589,12 +589,6 @@ for x in range(0,repolevel-1): def caterror(mycat): err(mycat+" is not an official category. Skipping QA checks in this directory.\nPlease ensure that you add "+catdir+" to "+repodir+"/profiles/categories\nif it is a new category.") -if mymode == "manifest": - pass -elif "--pretend" in myoptions: - print green("\nRepoMan does a once-over of the neighborhood...") -elif quiet < 1: - print green("\nRepoMan scours the neighborhood...") def parse_use_local_desc(mylines, usedict=None): """returns a dict of the form {cpv:set(flags)}""" @@ -816,6 +810,62 @@ def x11_deprecation_check(depstr): return True return False +if mymode == "commit": + retval = ("","") + if isCvs: + print + print "Performing a " + green("cvs -n up") + \ + " with a little magic grep to check for updates." + retval = getstatusoutput("cvs -n up 2>&1 | " + \ + "egrep '^[^\?] .*' | " + \ + "egrep -v '^. .*/digest-[^/]+|^cvs server: .* -- ignored$'") + + if isSvn: + print + print "Performing a "+green("svn status -u") + \ + " with a little magic grep to check for updates." + retval=getstatusoutput("svn status -u 2>&1 | " + \ + "egrep -v '^. +.*/digest-[^/]+' | " + \ + "head -n-1") + + mylines=retval[1].splitlines() + myupdates=[] + for x in mylines: + if not x: + continue + if x[0] not in "UPMARD": # Updates,Patches,Modified,Added,Removed/Replaced(svn),Deleted(svn) + print red("!!! Please fix the following issues reported " + \ + "from vcs: ")+green("(U,P,M,A,R,D are ok)") + print red("!!! Note: This is a pretend/no-modify pass...") + print retval[1] + print + sys.exit(1) + elif isCvs and x[0] in ["U","P"]: + myupdates.append(x[2:]) + elif isSvn and x[8] == '*': + myupdates.append(x[9:].lstrip(" 1234567890")) + + if myupdates: + print green("Fetching trivial updates...") + if "--pretend" in myoptions: + print "(cvs/svn up "+" ".join(myupdates)+")" + retval = os.EX_OK + else: + if isCvs: + retval=os.system("cvs up "+" ".join(myupdates)) + elif isSvn: + retval=os.system("svn update "+" ".join(myupdates)) + if retval != os.EX_OK: + print "!!! cvs/svn exited with an error. Terminating." + sys.exit(retval) + +if mymode == "manifest": + pass +elif "--pretend" in myoptions: + print green("\nRepoMan does a once-over of the neighborhood...") +elif quiet < 1: + print green("\nRepoMan scours the neighborhood...") + dofail = 0 arch_caches={} arch_xmatch_caches = {} @@ -1729,47 +1779,6 @@ elif "--pretend" not in myoptions: print sys.exit(1) - retval=["",""] - if isCvs: - print "Performing a "+green("cvs -n up")+" with a little magic grep to check for updates." - retval=getstatusoutput("cvs -n up 2>&1 | egrep '^[^\?] .*' | egrep -v '^. .*/digest-[^/]+|^cvs server: .* -- ignored$'") - if isSvn: - print "Performing a "+green("svn status -u")+" with a little magic grep to check for updates." - retval=getstatusoutput("svn status -u 2>&1 | egrep -v '^. +.*/digest-[^/]+' | head -n-1") - - mylines=retval[1].split("\n") - myupdates=[] - for x in mylines: - if not x: - continue - if x[0] not in "UPMARD": # Updates,Patches,Modified,Added,Removed/Replaced(svn),Deleted(svn) - print red("!!! Please fix the following issues reported from cvs: ")+green("(U,P,M,A,R,D are ok)") - if isCvs: - print red("!!! Note: This is a pretend/no-modify pass...") - if isSvn: - print red("!!! Note: This your local checkout has been modified!") - print retval[1] - print - sys.exit(1) - elif isCvs and x[0] in ["U","P"]: - myupdates+=[x[2:]] - elif isSvn and x[8] == '*': - myupdates+=[x[9:].lstrip(" 1234567890")] - - if myupdates: - print green("Fetching trivial updates...") - if "--pretend" in myoptions: - print "(cvs/svn up "+" ".join(myupdates)+")" - retval=0 - else: - if isCvs: - retval=os.system("cvs up "+" ".join(myupdates)) - elif isSvn: - retval=os.system("svn update "+" ".join(myupdates)) - if retval!=0: - print "!!! cvs/svn exited with an error. Terminating." - sys.exit(retval) - if isCvs: mycvstree=portage.cvstree.getentries("./",recursive=1) mychanged=portage.cvstree.findchanged(mycvstree,recursive=1,basedir="./") @@ -1785,8 +1794,7 @@ elif "--pretend" not in myoptions: if isCvs or isSvn: if not (mychanged or mynew or myremoved): - print - print green("RepoMan sez:"), "\"Doing nothing is not always good for QA.\"\n" + print green("RepoMan sez:"), "\"Doing nothing is not always good for QA.\"" print print "(Didn't find any changed files...)" print @@ -1969,6 +1977,23 @@ elif "--pretend" not in myoptions: else: raise portage.exception.PortageException("!!! gpg exited with '" + str(rValue) + "' status") + # When files are removed and re-added, the cvs server will put /Attic/ + # inside the $Header path. This code detects the problem and corrects it + # so that the Manifest will generate correctly. See bug #169500. + from portage.util import write_atomic + cvs_header = re.compile(r'^#\s*\$Header.*\$$') + for x in myheaders: + f = open(x) + mylines = f.readlines() + f.close() + modified = False + for i, line in enumerate(mylines): + if cvs_header.match(line) and "/Attic/" in line: + mylines[i] = line.replace("/Attic/", "/") + modified = True + if modified: + write_atomic(x, "".join(mylines)) + manifest_commit_required = True if myheaders or myupdates or myremoved or mynew: myfiles=myheaders+myupdates+myremoved+mynew diff --git a/man/emerge.1 b/man/emerge.1 index fed38d86e..6d6d7ca47 100644 --- a/man/emerge.1 +++ b/man/emerge.1 @@ -125,12 +125,10 @@ package data. It does not populate cache for the overlays listed in PORTDIR_OVERLAY. In order to generate cache for overlays, use \fB\-\-regen\fR. .TP .BR "\-\-prune " (\fB\-P\fR) -\fBWARNING: This action can remove important packages!\fR Prune looks at each -installed package and attempts to \fBremove\fR all but the \fBmost recently -installed version. \fRPrune ignores \fBslots\fR, if you require a slot\-aware -Prune use \fB emerge \-\-clean\fR. Prune may inadvertently remove important -packages from your system. Use \fB\-\-clean\fR instead unless you really -know what you're doing. +\fBWARNING: This action can remove important packages!\fR Removes all but the +highest installed version of a package from your system. This action doesn't +verify the possible binary compatibility between versions and can thus remove +essential dependencies from your system. .TP .BR \-\-regen Causes portage to check and update the dependency cache of all ebuilds in the diff --git a/man/portage.5 b/man/portage.5 index e1855efbb..78b2623d6 100644 --- a/man/portage.5 +++ b/man/portage.5 @@ -62,6 +62,7 @@ info_pkgs info_vars package.mask profiles.desc +repo_name thirdpartymirrors use.desc use.local.desc @@ -608,6 +609,11 @@ m68k default-linux/m68k dev x86 default-linux/x86/2004.3 stable .fi .TP +.BR repo_name +The first line of the file should define a unique repository name. The name +may contain any of the characters [A\-Za\-z0\-9_\-]. It must not begin with a +hyphen. +.TP .BR thirdpartymirrors Controls the mapping of mirror:// style URLs to actual lists of mirrors. Keeps us from overloading a single server. diff --git a/pym/emerge/__init__.py b/pym/emerge/__init__.py index 147e0463f..dbd84672e 100644 --- a/pym/emerge/__init__.py +++ b/pym/emerge/__init__.py @@ -2954,9 +2954,12 @@ class depgraph(object): if repo_name_prev: repo_path_prev = portdb.getRepositoryPath( repo_name_prev) - repoadd = "%s=>%s" % ( - repo_display.repoStr(repo_path_prev), - repo_display.repoStr(repo_path_real)) + if repo_path_prev == repo_path_real: + repoadd = repo_display.repoStr(repo_path_real) + else: + repoadd = "%s=>%s" % ( + repo_display.repoStr(repo_path_prev), + repo_display.repoStr(repo_path_real)) if repoadd: verboseadd += teal("[%s]" % repoadd) @@ -4023,9 +4026,32 @@ def unmerge(settings, myopts, vartree, unmerge_action, unmerge_files, if y not in pkgmap[mykey]["selected"]: pkgmap[mykey]["selected"].append(y) numselected=numselected+len(mymatch) - + elif unmerge_action == "prune": + if len(mymatch) == 1: + continue + best_version = mymatch[0] + best_slot = vartree.getslot(best_version) + best_counter = vartree.dbapi.cpv_counter(best_version) + for mypkg in mymatch[1:]: + myslot = vartree.getslot(mypkg) + mycounter = vartree.dbapi.cpv_counter(mypkg) + if (myslot == best_slot and mycounter > best_counter) or \ + mypkg == portage.best([mypkg, best_version]): + if myslot == best_slot: + if mycounter < best_counter: + # On slot collision, keep the one with the + # highest counter since it is the most + # recently installed. + continue + best_version = mypkg + best_slot = myslot + best_counter = mycounter + pkgmap[mykey]["protected"].append(best_version) + pkgmap[mykey]["selected"] = [mypkg for mypkg in mymatch \ + if mypkg != best_version] + numselected = numselected + len(pkgmap[mykey]["selected"]) else: - #unmerge_action in ["prune", clean"] + # unmerge_action == "clean" slotmap={} for mypkg in mymatch: if unmerge_action=="clean": @@ -5362,6 +5388,15 @@ def action_depclean(settings, trees, ldpath_mtimes, print return args_set.add(atom) + matched_packages = False + for x in args_set: + if vardb.match(x): + matched_packages = True + break + if not matched_packages: + portage.writemsg_stdout( + ">>> No packages selected for removal by %s\n" % action) + return if "--quiet" not in myopts: print "\nCalculating dependencies ", @@ -5380,10 +5415,16 @@ def action_depclean(settings, trees, ldpath_mtimes, # Pull in everything that's installed since we don't want to prune a # package if something depends on it. remaining_atoms.extend((atom, 'world', hard) for atom in vardb.cp_all()) + if not myfiles: + # Try to prune everything that's slotted. + for cp in vardb.cp_all(): + if len(vardb.cp_list(cp)) > 1: + args_set.add(cp) unresolveable = {} aux_keys = ["DEPEND", "RDEPEND", "PDEPEND"] metadata_keys = ["PROVIDE", "SLOT", "USE"] + graph = digraph() while remaining_atoms: atom, parent, priority = remaining_atoms.pop() @@ -5443,6 +5484,7 @@ def action_depclean(settings, trees, ldpath_mtimes, pkgs = visible_in_portdb pkgs = [portage.best(pkgs)] for pkg in pkgs: + graph.add(pkg, parent) if fakedb.cpv_exists(pkg): continue spinner.update() @@ -5496,16 +5538,29 @@ def action_depclean(settings, trees, ldpath_mtimes, print for atom in unresolveable: print atom, "required by", " ".join(unresolveable[atom]) - if unresolveable and action == "depclean": + if unresolveable: print print "Have you forgotten to run " + good("`emerge --update --newuse --deep world`") + " prior to" - print "depclean? It may be necessary to manually uninstall packages that no longer" + print "%s? It may be necessary to manually uninstall packages that no longer" % action print "exist in the portage tree since it may not be possible to satisfy their" print "dependencies. Also, be aware of the --with-bdeps option that is documented" print "in " + good("`man emerge`") + "." print + if action == "prune": + print "If you would like to ignore dependencies then use %s." % \ + good("--nodeps") return + def show_parents(child_node): + parent_nodes = graph.parent_nodes(child_node) + parent_nodes.sort() + msg = [] + msg.append(" %s pulled in by:\n" % str(child_node)) + for parent_node in parent_nodes: + msg.append(" %s\n" % str(parent_node)) + msg.append("\n") + portage.writemsg_stdout("".join(msg), noiselevel=-1) + cleanlist = [] if action == "depclean": if myfiles: @@ -5518,20 +5573,39 @@ def action_depclean(settings, trees, ldpath_mtimes, except portage.exception.InvalidDependString: # this error has already been displayed by now continue - if arg_atom and not fakedb.cpv_exists(pkg): - cleanlist.append(pkg) + if arg_atom: + if not fakedb.cpv_exists(pkg): + cleanlist.append(pkg) + elif "--verbose" in myopts: + show_parents(pkg) else: for pkg in vardb.cpv_all(): if not fakedb.cpv_exists(pkg): cleanlist.append(pkg) + elif "--verbose" in myopts: + show_parents(pkg) elif action == "prune": + # Prune really uses all installed instead of world. It's not a real + # reverse dependency so don't display it as such. + graph.remove("world") for atom in args_set: for pkg in vardb.match(atom): if not fakedb.cpv_exists(pkg): cleanlist.append(pkg) - if not cleanlist: + elif "--verbose" in myopts: + show_parents(pkg) + + if not cleanlist: + portage.writemsg_stdout( + ">>> No packages selected for removal by %s\n" % action) + if "--verbose" not in myopts: portage.writemsg_stdout( - ">>> No packages selected for removal by %s\n" % action) + ">>> To see reverse dependencies, use %s\n" % \ + good("--verbose")) + if action == "prune": + portage.writemsg_stdout( + ">>> To ignore dependencies, use %s\n" % \ + good("--nodeps")) if len(cleanlist): unmerge(settings, myopts, trees[settings["ROOT"]]["vartree"], diff --git a/pym/emerge/help.py b/pym/emerge/help.py index 7502d423d..def367fd5 100644 --- a/pym/emerge/help.py +++ b/pym/emerge/help.py @@ -87,7 +87,7 @@ def help(myaction,myopts,havecolor=1): print print " "+green("--prune")+" ("+green("-P")+" short option)" print " "+turquoise("WARNING: This action can remove important packages!") - print " Removes all but the most recently installed version of a package" + print " Removes all but the highest installed version of a package" print " from your system. This action doesn't verify the possible binary" print " compatibility between versions and can thus remove essential" print " dependencies from your system." diff --git a/pym/portage/__init__.py b/pym/portage/__init__.py index be46009d4..6f1df54ee 100644 --- a/pym/portage/__init__.py +++ b/pym/portage/__init__.py @@ -2774,9 +2774,15 @@ def fetch(myuris, mysettings, listonly=0, fetchonly=0, locks_in_subdir=".locks", if reason[0] == "Insufficient data for checksum verification": return 0 if can_fetch and not restrict_fetch: - writemsg("Refetching...\n\n", - noiselevel=-1) - os.unlink(myfile_path) + from tempfile import mkstemp + fd, temp_filename = mkstemp("", + myfile + "._checksum_failure_.", + mysettings["DISTDIR"]) + os.close(fd) + os.rename(myfile_path, temp_filename) + writemsg_stdout("Refetching... " + \ + "File renamed to '%s'\n\n" % \ + temp_filename, noiselevel=-1) else: eout = portage.output.EOutput() eout.quiet = \ @@ -2842,6 +2848,14 @@ def fetch(myuris, mysettings, listonly=0, fetchonly=0, locks_in_subdir=".locks", myfetch = [varexpand(x, mydict=variables) for x in lexer] spawn_keywords = {} + # Redirect all output to stdout since some fetchers like + # wget pollute stderr (if portage detects a problem then it + # can send it's own message to stderr). + spawn_keywords["fd_pipes"] = { + 0:sys.stdin.fileno(), + 1:sys.stdout.fileno(), + 2:sys.stdout.fileno() + } if "userfetch" in mysettings.features and \ os.getuid() == 0 and portage_gid and portage_uid: spawn_keywords.update({ @@ -2849,7 +2863,7 @@ def fetch(myuris, mysettings, listonly=0, fetchonly=0, locks_in_subdir=".locks", "gid" : portage_gid, "groups" : userpriv_groups, "umask" : 002}) - + myret = -1 try: if mysettings.selinux_enabled(): @@ -2898,7 +2912,15 @@ def fetch(myuris, mysettings, listonly=0, fetchonly=0, locks_in_subdir=".locks", else: # no exception? file exists. let digestcheck() report # an appropriately for size or checksum errors - if (mystat[stat.ST_SIZE]4) and not ((myfile[-5:]==".html") or (myfile[-4:]==".htm")): html404=re.compile(".*(not found|404).*",re.I|re.M) @@ -2931,8 +2953,15 @@ def fetch(myuris, mysettings, listonly=0, fetchonly=0, locks_in_subdir=".locks", (reason[1], reason[2]), noiselevel=-1) if reason[0] == "Insufficient data for checksum verification": return 0 - writemsg("Removing corrupt distfile...\n", noiselevel=-1) - os.unlink(mysettings["DISTDIR"]+"/"+myfile) + from tempfile import mkstemp + fd, temp_filename = mkstemp("", + myfile + "._checksum_failure_.", + mysettings["DISTDIR"]) + os.close(fd) + os.rename(myfile_path, temp_filename) + writemsg_stdout("Refetching... " + \ + "File renamed to '%s'\n\n" % \ + temp_filename, noiselevel=-1) fetched=0 else: eout = portage.output.EOutput() diff --git a/pym/portage/sets/__init__.py b/pym/portage/sets/__init__.py index 29dc60c07..3e1fbdb2e 100644 --- a/pym/portage/sets/__init__.py +++ b/pym/portage/sets/__init__.py @@ -77,9 +77,12 @@ class PackageSet(object): else: return "" - def _updateAtomMap(self): - self._atommap.clear() - for a in self._atoms: + def _updateAtomMap(self, atoms=None): + """Update self._atommap for specific atoms or all atoms.""" + if not atoms: + self._atommap.clear() + atoms = self._atoms + for a in atoms: cp = dep_getkey(a) self._atommap.setdefault(cp, set()) self._atommap[cp].add(a) @@ -118,7 +121,7 @@ class EditablePackageSet(PackageSet): def update(self, atoms): self.getAtoms() self._atoms.update(atoms) - self._updateAtomMap() + self._updateAtomMap(atoms=atoms) self.write() def add(self, atom):