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
==================================
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)}"""
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 = {}
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="./")
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
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
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
info_vars
package.mask
profiles.desc
+repo_name
thirdpartymirrors
use.desc
use.local.desc
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.
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)
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":
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 ",
# 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()
pkgs = visible_in_portdb
pkgs = [portage.best(pkgs)]
for pkg in pkgs:
+ graph.add(pkg, parent)
if fakedb.cpv_exists(pkg):
continue
spinner.update()
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:
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"],
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."
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 = \
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({
"gid" : portage_gid,
"groups" : userpriv_groups,
"umask" : 002})
-
+ myret = -1
try:
if mysettings.selinux_enabled():
else:
# no exception? file exists. let digestcheck() report
# an appropriately for size or checksum errors
- if (mystat[stat.ST_SIZE]<mydigests[myfile]["size"]):
+
+ # If the fetcher reported success and the file is
+ # too small, it's probably because the digest is
+ # bad (upstream changed the distfile). In this
+ # case we don't want to attempt to resume. Show a
+ # digest verification failure to that the user gets
+ # a clue about what just happened.
+ if myret != os.EX_OK and \
+ mystat.st_size < mydigests[myfile]["size"]:
# Fetch failed... Try the next one... Kill 404 files though.
if (mystat[stat.ST_SIZE]<100000) and (len(myfile)>4) and not ((myfile[-5:]==".html") or (myfile[-4:]==".htm")):
html404=re.compile("<title>.*(not found|404).*</title>",re.I|re.M)
(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()
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)
def update(self, atoms):
self.getAtoms()
self._atoms.update(atoms)
- self._updateAtomMap()
+ self._updateAtomMap(atoms=atoms)
self.write()
def add(self, atom):