summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorZac Medico <zmedico@gentoo.org>2009-04-30 07:19:02 +0000
committerZac Medico <zmedico@gentoo.org>2009-04-30 07:19:02 +0000
commitabd693991aa185c0b2259f814feb9f3df55187ae (patch)
tree8dfd273afeace1c9f903aa574c7424df33686910
parentFix default master code so that it doesn't trigger when PORTDIR is empty. (diff)
downloadportage-multirepo-abd693991aa185c0b2259f814feb9f3df55187ae.tar.gz
portage-multirepo-abd693991aa185c0b2259f814feb9f3df55187ae.tar.bz2
portage-multirepo-abd693991aa185c0b2259f814feb9f3df55187ae.zip
Add a new --deselect action which removes atoms from the world file. This
action is implied by uninstall actions, including --depclean, --prune and --unmerge. Use --deselect=n in order to prevent uninstall actions from removing atoms from the world file. This solves bug #259994 and bug #265206. (trunk r13363) svn path=/main/branches/2.1.6/; revision=13519
-rw-r--r--man/emerge.17
-rw-r--r--pym/_emerge/__init__.py78
-rw-r--r--pym/_emerge/help.py12
-rw-r--r--pym/portage/dep.py30
4 files changed, 118 insertions, 9 deletions
diff --git a/man/emerge.1 b/man/emerge.1
index 8b2e7c16..a085f8a2 100644
--- a/man/emerge.1
+++ b/man/emerge.1
@@ -130,6 +130,13 @@ unmerge matched packages that have no reverse dependencies. Use
\fB\-\-depclean\fR together with \fB\-\-verbose\fR to show reverse
dependencies.
.TP
+.BR "\-\-deselect[=n]"
+Remove atoms from the world file. This action is implied
+by uninstall actions, including \fB-\-depclean\fR,
+\fB-\-prune\fR and \fB-\-unmerge\fR. Use \fB-\-deselect=n\fR
+in order to prevent uninstall actions from removing
+atoms from the world file.
+.TP
.BR "\-\-help " (\fB\-h\fR)
Displays help information for emerge. Adding one of the additional
arguments listed above will give you more specific help information
diff --git a/pym/_emerge/__init__.py b/pym/_emerge/__init__.py
index 03cef509..3ba88ecc 100644
--- a/pym/_emerge/__init__.py
+++ b/pym/_emerge/__init__.py
@@ -11837,6 +11837,8 @@ def unmerge(root_config, myopts, unmerge_action,
clean_world=1, clean_delay=1, ordered=0, raise_on_error=0,
scheduler=None, writemsg_level=portage.util.writemsg_level):
+ if clean_world:
+ clean_world = myopts.get('--deselect') != 'n'
quiet = "--quiet" in myopts
settings = root_config.settings
sets = root_config.sets
@@ -13873,12 +13875,50 @@ def action_uninstall(settings, trees, ldpath_mtimes,
unmerge(trees[settings["ROOT"]]['root_config'], opts, action,
valid_atoms, ldpath_mtimes, ordered=ordered)
rval = os.EX_OK
+ elif action == 'deselect':
+ rval = action_deselect(settings, trees, opts, valid_atoms)
else:
rval = action_depclean(settings, trees, ldpath_mtimes,
opts, action, valid_atoms, spinner)
return rval
+def action_deselect(settings, trees, opts, atoms):
+ world_set = trees[settings['ROOT']]['root_config'].sets['world']
+ if not hasattr(world_set, 'update'):
+ writemsg_level("World set does not appear to be mutable.\n",
+ level=logging.ERROR, noiselevel=-1)
+ return 1
+ locked = False
+ if not hasattr(world_set, 'lock'):
+ world_set.lock()
+ locked = True
+ try:
+ discard_atoms = set()
+ world_set.load()
+ from portage.dep import Atom
+ for atom in world_set:
+ if not isinstance(atom, Atom):
+ # nested set
+ continue
+ for arg_atom in atoms:
+ if arg_atom.intersects(atom):
+ discard_atoms.add(atom)
+ break
+ if discard_atoms:
+ for atom in sorted(discard_atoms):
+ print ">>> Removing %s from \"world\" favorites file..." % \
+ colorize("INFORM", str(atom))
+ remaining = set(world_set)
+ remaining.difference_update(discard_atoms)
+ world_set.replace(remaining)
+ else:
+ print ">>> No matching atoms found in \"world\" favorites file..."
+ finally:
+ if locked:
+ world_set.unlock()
+ return os.EX_OK
+
def action_depclean(settings, trees, ldpath_mtimes,
myopts, action, myfiles, spinner):
# Kill packages that aren't explicitly merged or are required as a
@@ -13921,6 +13961,7 @@ def action_depclean(settings, trees, ldpath_mtimes,
root_config = trees[myroot]["root_config"]
getSetAtoms = root_config.setconfig.getSetAtoms
vardb = trees[myroot]["vartree"].dbapi
+ deselect = myopts.get('--deselect') != 'n'
required_set_names = ("system", "world")
required_sets = {}
@@ -13978,11 +14019,13 @@ def action_depclean(settings, trees, ldpath_mtimes,
if action == "depclean":
if args_set:
+
+ if deselect:
+ world_temp_set.clear()
+
# Pull in everything that's installed but not matched
# by an argument atom since we don't want to clean any
# package if something depends on it.
-
- world_temp_set.clear()
for pkg in vardb:
spinner.update()
@@ -13999,9 +14042,11 @@ def action_depclean(settings, trees, ldpath_mtimes,
elif action == "prune":
+ if deselect:
+ world_temp_set.clear()
+
# Pull in everything that's installed since we don't
# to prune a package if something depends on it.
- world_temp_set.clear()
world_temp_set.update(vardb.cp_all())
if not args_set:
@@ -14820,16 +14865,19 @@ def insert_optional_args(args):
new_args = []
jobs_opts = ("-j", "--jobs")
- root_deps_opt = '--root-deps'
- root_deps_choices = ('True', 'rdeps')
+ default_arg_opts = {
+ '--deselect' : ('n',),
+ '--root-deps' : ('rdeps',),
+ }
arg_stack = args[:]
arg_stack.reverse()
while arg_stack:
arg = arg_stack.pop()
- if arg == root_deps_opt:
+ default_arg_choices = default_arg_opts.get(arg)
+ if default_arg_choices is not None:
new_args.append(arg)
- if arg_stack and arg_stack[-1] in root_deps_choices:
+ if arg_stack and arg_stack[-1] in default_arg_choices:
new_args.append(arg_stack.pop())
else:
# insert default argument
@@ -14897,6 +14945,12 @@ def parse_opts(tmpcmdline, silent=False):
"choices":("y", "n")
},
+ "--deselect": {
+ "help" : "remove atoms from the world file",
+ "type" : "choice",
+ "choices" : ("True", "n")
+ },
+
"--jobs": {
"help" : "Specifies the number of packages to build " + \
@@ -14962,6 +15016,9 @@ def parse_opts(tmpcmdline, silent=False):
myoptions, myargs = parser.parse_args(args=tmpcmdline)
+ if myoptions.deselect == "True":
+ myoptions.deselect = True
+
if myoptions.root_deps == "True":
myoptions.root_deps = True
@@ -15019,6 +15076,9 @@ def parse_opts(tmpcmdline, silent=False):
sys.exit(1)
myaction = action_opt
+ if myaction is None and myoptions.deselect is True:
+ myaction = 'deselect'
+
myfiles += myargs
return myaction, myopts, myfiles
@@ -15649,11 +15709,11 @@ def emerge_main():
action_search(trees[settings["ROOT"]]["root_config"],
myopts, myfiles, spinner)
- elif myaction in ('clean', 'depclean', 'prune', 'unmerge'):
+ elif myaction in ('clean', 'depclean', 'deselect', 'prune', 'unmerge'):
validate_ebuild_environment(trees)
rval = action_uninstall(settings, trees, mtimedb["ldpath"],
myopts, myaction, myfiles, spinner)
- if not (buildpkgonly or fetchonly or pretend):
+ if not (myaction == 'deselect' or buildpkgonly or fetchonly or pretend):
post_emerge(root_config, myopts, mtimedb, rval)
return rval
diff --git a/pym/_emerge/help.py b/pym/_emerge/help.py
index f1afef8b..66bc2f33 100644
--- a/pym/_emerge/help.py
+++ b/pym/_emerge/help.py
@@ -109,6 +109,18 @@ def help(myaction,myopts,havecolor=1):
for line in wrap(paragraph, desc_width):
print desc_indent + line
print
+ print " " + green("--deselect") + "[=%s]" % turquoise("n")
+
+ paragraph = \
+ "Remove atoms from the world file. This action is implied " + \
+ "by uninstall actions, including --depclean, " + \
+ "--prune and --unmerge. Use --deselect=n " + \
+ "in order to prevent uninstall actions from removing " + \
+ "atoms from the world file."
+
+ for line in wrap(paragraph, desc_width):
+ print desc_indent + line
+ print
print " "+green("--info")
print " Displays important portage variables that will be exported to"
print " ebuild.sh when performing merges. This information is useful"
diff --git a/pym/portage/dep.py b/pym/portage/dep.py
index 86625871..80ebdadb 100644
--- a/pym/portage/dep.py
+++ b/pym/portage/dep.py
@@ -558,6 +558,36 @@ class Atom(object):
raise AttributeError("Atom instances are immutable",
self.__class__, name, value)
+ def intersects(self, other):
+ """
+ Atoms with different operator or cpv attributes cause this method to
+ return False. TODO: Detect intersection when operators are present.
+ @param other: The package atom to match
+ @type other: Atom
+ @rtype: Boolean
+ @return: True if this atom and the other atom intersect,
+ False otherwise.
+ """
+ if not isinstance(other, Atom):
+ raise TypeError("expected %s, got %s" % \
+ (Atom, type(other)))
+
+ if self == other:
+ return True
+
+ if self.cp != other.cp or \
+ self.use != other.use or \
+ self.operator != other.operator or \
+ self.cpv != other.cpv:
+ return False
+
+ if self.slot is None or \
+ other.slot is None or \
+ self.slot == other.slot:
+ return True
+
+ return False
+
# Implement some common str methods.
def __eq__(self, other):