summaryrefslogtreecommitdiffstats
path: root/pym
diff options
context:
space:
mode:
authorZac Medico <zmedico@gentoo.org>2009-04-19 05:44:22 +0000
committerZac Medico <zmedico@gentoo.org>2009-04-19 05:44:22 +0000
commit8dbcc983b98ecc78a4396ae98c4219ed5128d5ae (patch)
tree331f76d8f515ceb71ff5504e7f156b32b06b57a4 /pym
parent5ebcc3927e39b4531c8633d7d8c0a377e7e174b1 (diff)
downloadportage-8dbcc983b98ecc78a4396ae98c4219ed5128d5ae.tar.gz
portage-8dbcc983b98ecc78a4396ae98c4219ed5128d5ae.tar.bz2
portage-8dbcc983b98ecc78a4396ae98c4219ed5128d5ae.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. svn path=/main/trunk/; revision=13363
Diffstat (limited to 'pym')
-rw-r--r--pym/_emerge/__init__.py78
-rw-r--r--pym/_emerge/help.py12
-rw-r--r--pym/portage/dep.py30
3 files changed, 111 insertions, 9 deletions
diff --git a/pym/_emerge/__init__.py b/pym/_emerge/__init__.py
index 3a8f110d0..f8dfaee02 100644
--- a/pym/_emerge/__init__.py
+++ b/pym/_emerge/__init__.py
@@ -11853,6 +11853,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
@@ -14049,12 +14051,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
@@ -14093,6 +14133,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 = {}
@@ -14150,11 +14191,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()
@@ -14171,9 +14214,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:
@@ -15175,16 +15220,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
@@ -15252,6 +15300,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 " + \
@@ -15317,6 +15371,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
@@ -15374,6 +15431,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
@@ -16140,11 +16200,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 fd57cc3be..4afece3a3 100644
--- a/pym/_emerge/help.py
+++ b/pym/_emerge/help.py
@@ -106,6 +106,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 866258715..80ebdadbc 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):