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.")
-# setup a uselist from portage
-uselist=[]
-try:
- uselist=portage.grabfile(portdir+"/profiles/use.desc")
- for l in range(0,len(uselist)):
- uselist[l]=uselist[l].split()[0]
- for var in repoman_settings["USE_EXPAND"].split():
- vardescs = portage.grabfile(portdir+"/profiles/desc/"+var.lower()+".desc")
- for l in range(0, len(vardescs)):
- uselist.append(var.lower() + "_" + vardescs[l].split()[0])
-except (IOError, OSError, ParseError), e:
- logging.exception("Couldn't read USE flags from use.desc")
- sys.exit(1)
+class ProfileDesc(object):
+ __slots__ = ('abs_path', 'arch', 'status', 'sub_path', 'tree_path',)
+ def __init__(self, arch, status, sub_path, tree_path):
+ self.arch = arch
+ self.status = status
+ self.sub_path = normalize_path(sub_path.lstrip(os.sep))
+ self.tree_path = tree_path
+ self.abs_path = os.path.join(tree_path, 'profiles', self.sub_path)
+
+profile_list = []
+valid_profile_types = frozenset(['dev', 'exp', 'stable'])
+
+# get lists of valid keywords, licenses, and use
+kwlist = set()
+liclist = set()
+uselist = set()
+
+for path in portdb.porttrees:
+ try:
+ liclist.update(os.listdir(os.path.join(path, "licenses")))
+ except OSError:
+ pass
+ kwlist.update(portage.grabfile(os.path.join(path,
+ "profiles", "arch.list")))
+
+ use_desc = portage.grabfile(os.path.join(path, 'profiles', 'use.desc'))
+ for x in use_desc:
+ x = x.split()
+ if x:
+ uselist.add(x[0])
+
+ expand_desc_dir = os.path.join(path, 'profiles', 'desc')
+ try:
+ expand_list = os.listdir(expand_desc_dir)
+ except OSError:
+ pass
+ else:
+ for fn in expand_list:
+ if not fn[-5:] == '.desc':
+ continue
+ use_prefix = fn[:-5].lower() + '_'
+ for x in portage.grabfile(os.path.join(expand_desc_dir, fn)):
+ x = x.split()
+ if x:
+ uselist.add(use_prefix + x[0])
-# get lists of valid keywords and licenses
-kwlist = set(portage.grabfile(os.path.join(portdir, "profiles", "arch.list")))
-liclist = set(portage.listdir(os.path.join(portdir, "licenses")))
+ desc_path = os.path.join(path, 'profiles', 'profiles.desc')
+ try:
+ desc_file = codecs.open(desc_path, mode='r', errors='replace')
+ except EnvironmentError:
+ pass
+ else:
+ for i, x in enumerate(desc_file):
+ if x[0] == "#":
+ continue
+ arch = x.split()
+ if len(arch) == 0:
+ continue
+ if len(arch) != 3:
+ err("wrong format: \"" + bad(x.strip()) + "\" in " + \
+ desc_path + " line %d" % (i+1, ))
+ elif arch[0] not in kwlist:
+ err("invalid arch: \"" + bad(arch[0]) + "\" in " + \
+ desc_path + " line %d" % (i+1, ))
+ elif arch[2] not in valid_profile_types:
+ err("invalid profile type: \"" + bad(arch[2]) + "\" in " + \
+ desc_path + " line %d" % (i+1, ))
+ profile_desc = ProfileDesc(arch[0], arch[2], arch[1], portdir)
+ if not os.path.isdir(profile_desc.abs_path):
+ logging.error(
+ "Invalid %s profile (%s) for arch %s in %s line %d",
+ arch[2], arch[1], arch[0], desc_path, i+1)
+ continue
+ profile_list.append(profile_desc)
+ desc_file.close()
+
+repoman_settings['PORTAGE_ARCHLIST'] = ' '.join(sorted(kwlist))
+repoman_settings.backup_changes('PORTAGE_ARCHLIST')
+
+# Ensure that profile sub_path attributes are unique. Process in reverse order
+# so that profiles with duplicate sub_path from overlays will override
+# profiles with the same sub_path from parent repos.
+profiles = {}
+profile_list.reverse()
+profile_sub_paths = set()
+for prof in profile_list:
+ if prof.sub_path in profile_sub_paths:
+ continue
+ profile_sub_paths.add(prof.sub_path)
+ profiles.setdefault(prof.arch, []).append(prof)
-if portdir_overlay != portdir:
- for porttree in portdb.porttrees[1:]:
- try:
- liclist.update(os.listdir(os.path.join(porttree, "licenses")))
- except OSError:
- pass
- kwlist.update(portage.grabfile(os.path.join(porttree,
- "profiles", "arch.list")))
+for x in repoman_settings.archlist():
+ if x[0] == "~":
+ continue
+ if x not in profiles:
+ print red("\""+x+"\" doesn't have a valid profile listed in profiles.desc.")
+ print red("You need to either \"cvs update\" your profiles dir or follow this")
+ print red("up with the "+x+" team.")
+ print
if not liclist:
logging.fatal("Couldn't find licenses?")
logging.fatal("Couldn't read KEYWORDS from arch.list")
sys.exit(1)
+if not uselist:
+ logging.fatal("Couldn't find use.desc?")
+ sys.exit(1)
+
scanlist=[]
if repolevel==2:
#we are inside a category directory
logging.debug("Found the following packages to scan:\n%s" % '\n'.join(scanlist))
-profiles={}
-valid_profile_types = frozenset(["dev", "exp", "stable"])
-descfile=portdir+"/profiles/profiles.desc"
-if os.path.exists(descfile):
- for i, x in enumerate(open(descfile)):
- if x[0]=="#":
- continue
- arch=x.split()
- if len(arch) == 0:
- continue
- if len(arch)!=3:
- err("wrong format: \"" + bad(x.strip()) + "\" in " + \
- descfile + " line %d" % (i+1, ))
- elif arch[0] not in kwlist:
- err("invalid arch: \"" + bad(arch[0]) + "\" in " + \
- descfile + " line %d" % (i+1, ))
- elif arch[2] not in valid_profile_types:
- err("invalid profile type: \"" + bad(arch[2]) + "\" in " + \
- descfile + " line %d" % (i+1, ))
- if not os.path.isdir(portdir+"/profiles/"+arch[1]):
- print "Invalid "+arch[2]+" profile ("+arch[1]+") for arch "+arch[0]
- continue
- if arch[0] in profiles:
- profiles[arch[0]]+= [[arch[1], arch[2]]]
- else:
- profiles[arch[0]] = [[arch[1], arch[2]]]
-
- for x in repoman_settings.archlist():
- if x[0] == "~":
- continue
- if x not in profiles:
- print red("\""+x+"\" doesn't have a valid profile listed in profiles.desc.")
- print red("You need to either \"cvs update\" your profiles dir or follow this")
- print red("up with the "+x+" team.")
- print
-else:
- print red("profiles.desc does not exist: "+descfile)
- print red("You need to do \"cvs update\" in profiles dir.")
- print
- sys.exit(1)
-
def dev_keywords(profiles):
"""
Create a set of KEYWORDS values that exist in 'dev'
"""
type_arch_map = {}
for arch, arch_profiles in profiles.iteritems():
- for profile_path, profile_type in arch_profiles:
- arch_set = type_arch_map.get(profile_type)
+ for prof in arch_profiles:
+ arch_set = type_arch_map.get(prof.status)
if arch_set is None:
arch_set = set()
- type_arch_map[profile_type] = arch_set
+ type_arch_map[prof.status] = arch_set
arch_set.add(arch)
dev_keywords = type_arch_map.get('dev', set())
for prof in profiles[arch]:
- if prof[1] not in ("stable", "dev") or \
- prof[1] == "dev" and not options.include_dev:
+ if prof.status not in ("stable", "dev") or \
+ prof.status == "dev" and not options.include_dev:
continue
- profdir = portdir+"/profiles/"+prof[0]
-
- if prof[0] in arch_caches:
- dep_settings = arch_caches[prof[0]]
- else:
+ dep_settings = arch_caches.get(prof.sub_path)
+ if dep_settings is None:
dep_settings = portage.config(
- config_profile_path=profdir,
+ config_profile_path=prof.abs_path,
config_incrementals=portage.const.INCREMENTALS,
local_config=False,
env=env)
if options.without_mask:
dep_settings.pmaskdict.clear()
- arch_caches[prof[0]] = dep_settings
+ arch_caches[prof.sub_path] = dep_settings
while True:
try:
# Protect ACCEPT_KEYWORDS from config.regenerate()
except ValueError:
break
- xmatch_cache_key = (prof[0], tuple(groups))
+ xmatch_cache_key = (prof.sub_path, tuple(groups))
xcache = arch_xmatch_caches.get(xmatch_cache_key)
if xcache is None:
portdb.melt()
prov_cp = portage.dep_getkey(myprovide)
if prov_cp not in dep_settings.getvirtuals():
stats["virtual.unavailable"]+=1
- fails["virtual.unavailable"].append(x+"/"+y+".ebuild: "+keyword+"("+prof[0]+") "+prov_cp)
+ fails["virtual.unavailable"].append("%s: %s(%s) %s" % \
+ (relative_path, keyword, prof.sub_path, prov_cp))
if not baddepsyntax:
ismasked = os.path.join(catdir, y) not in \
have_dev_keywords = \
bool(dev_keywords.intersection(keywords))
- if prof[1] == "dev":
+ if prof.status == "dev":
suffix=suffix+"indev"
for mytype,mypos in [["DEPEND",len(missingvars)],["RDEPEND",len(missingvars)+1],["PDEPEND",len(missingvars)+2]]:
myvalue = myaux[mytype]
if not myvalue:
continue
- try:
- mydep = portage.dep_check(myvalue, portdb,
- dep_settings, use="all", mode=matchmode,
- trees=trees)
- except KeyError, e:
- stats[mykey]=stats[mykey]+1
- fails[mykey].append(x+"/"+y+".ebuild: "+keyword+"("+prof[0]+") "+repr(e[0]))
- continue
-
- if mydep[0]==1:
- if mydep[1]!=[]:
+
+ success, atoms = portage.dep_check(myvalue, portdb,
+ dep_settings, use="all", mode=matchmode,
+ trees=trees)
+
+ if success:
+ if atoms:
#we have some unsolvable deps
#remove ! deps, which always show up as unsatisfiable
- d=0
- while d<len(mydep[1]):
- if mydep[1][d][0]=="!":
- del mydep[1][d]
- else:
- d += 1
+ atoms = [str(atom) for atom in atoms if not atom.blocker]
#if we emptied out our list, continue:
- if not mydep[1]:
+ if not atoms:
continue
stats[mykey]=stats[mykey]+1
- fails[mykey].append(x+"/"+y+".ebuild: "+keyword+"("+prof[0]+") "+repr(mydep[1]))
+ fails[mykey].append("%s: %s(%s) %s" % \
+ (relative_path, keyword,
+ prof.sub_path, repr(atoms)))
else:
stats[mykey]=stats[mykey]+1
- fails[mykey].append(x+"/"+y+".ebuild: "+keyword+"("+prof[0]+") "+repr(mydep[1]))
+ fails[mykey].append("%s: %s(%s) %s" % \
+ (relative_path, keyword,
+ prof.sub_path, repr(atoms)))
# Check for 'all unstable' or 'all masked' -- ACCEPT_KEYWORDS is stripped
# XXX -- Needs to be implemented in dep code. Can't determine ~arch nicely.
import portage.util
import portage.locks
import portage.exception
+from portage.cache.cache_errors import CacheError
from portage.const import EPREFIX, BPREFIX, EPREFIX_LSTRIP
from portage.data import secpass
from portage.elog.messages import eerror
xtermTitle(" ".join(plain_output.split()))
+class ProgressHandler(object):
+ def __init__(self):
+ self.curval = 0
+ self.maxval = 0
+ self._last_update = 0
+ self.min_latency = 0.2
+
+ def onProgress(self, maxval, curval):
+ self.maxval = maxval
+ self.curval = curval
+ cur_time = time.time()
+ if cur_time - self._last_update >= self.min_latency:
+ self._last_update = cur_time
+ self.display()
+
+ def display(self):
+ raise NotImplementedError(self)
+
class Scheduler(PollScheduler):
_opts_ignore_blockers = \
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
def action_sync(settings, trees, mtimedb, myopts, myaction):
xterm_titles = "notitles" not in settings.features
emergelog(xterm_titles, " === sync")
- myportdir = settings.get("PORTDIR", None)
+ portdb = trees[settings["ROOT"]]["porttree"].dbapi
+ myportdir = portdb.porttree_root
out = portage.output.EOutput()
if not myportdir:
sys.stderr.write("!!! PORTDIR is undefined. Is /etc/make.globals missing?\n")
root_config = trees[settings["ROOT"]]["root_config"]
portdb = trees[settings["ROOT"]]["porttree"].dbapi
- if os.path.exists(myportdir+"/metadata/cache") and updatecache_flg:
- action_metadata(settings, portdb, myopts)
+ if updatecache_flg and \
+ os.path.exists(os.path.join(myportdir, 'metadata', 'cache')):
+
+ # Only update cache for myportdir since that's
+ # the only one that's been synced here.
+ action_metadata(settings, portdb, myopts, porttrees=[myportdir])
if portage._global_updates(trees, mtimedb["updates"]):
mtimedb.commit()
return os.EX_OK
-def action_metadata(settings, portdb, myopts):
- portage.writemsg_stdout("\n>>> Updating Portage cache: ")
+def action_metadata(settings, portdb, myopts, porttrees=None):
+ if porttrees is None:
+ porttrees = portdb.porttrees
+ portage.writemsg_stdout("\n>>> Updating Portage cache\n")
old_umask = os.umask(0002)
cachedir = os.path.normpath(settings.depcachedir)
if cachedir in ["/", "/bin", "/dev", "/etc", "/home",
"!!! This is ALMOST CERTAINLY NOT what you want: '%s'" % cachedir
sys.exit(73)
if not os.path.exists(cachedir):
- os.mkdir(cachedir)
-
- ec = portage.eclass_cache.cache(portdb.porttree_root)
- myportdir = os.path.realpath(settings["PORTDIR"])
- cm = settings.load_best_module("portdbapi.metadbmodule")(
- myportdir, "metadata/cache", portage.auxdbkeys[:], readonly=True)
-
- from portage.cache import util
-
- class percentage_noise_maker(util.quiet_mirroring):
- def __init__(self, dbapi):
- self.dbapi = dbapi
- self.cp_all = dbapi.cp_all()
- l = len(self.cp_all)
- self.call_update_min = 100000000
- self.min_cp_all = l/100.0
- self.count = 1
- self.pstr = ''
-
- def __iter__(self):
- for x in self.cp_all:
- self.count += 1
- if self.count > self.min_cp_all:
- self.call_update_min = 0
- self.count = 0
- for y in self.dbapi.cp_list(x):
- yield y
- self.call_update_mine = 0
-
- def update(self, *arg):
+ os.makedirs(cachedir)
+
+ auxdbkeys = [x for x in portage.auxdbkeys if not x.startswith("UNUSED_0")]
+ auxdbkeys = tuple(auxdbkeys)
+
+ class TreeData(object):
+ __slots__ = ('dest_db', 'eclass_db', 'path', 'src_db', 'valid_nodes')
+ def __init__(self, dest_db, eclass_db, path, src_db):
+ self.dest_db = dest_db
+ self.eclass_db = eclass_db
+ self.path = path
+ self.src_db = src_db
+ self.valid_nodes = set()
+
+ porttrees_data = []
+ for path in porttrees:
+ src_db = portdb._pregen_auxdb.get(path)
+ if src_db is None and \
+ os.path.isdir(os.path.join(path, 'metadata', 'cache')):
+ src_db = portdb.metadbmodule(
+ path, 'metadata/cache', auxdbkeys, readonly=True)
try:
- self.pstr = int(self.pstr) + 1
- except ValueError:
- self.pstr = 1
- sys.stdout.write("%s%i%%" % \
- ("\b" * (len(str(self.pstr))+1), self.pstr))
- sys.stdout.flush()
- self.call_update_min = 10000000
+ src_db.ec = portdb._repo_info[path].eclass_db
+ except AttributeError:
+ pass
- def finish(self, *arg):
- sys.stdout.write("\b\b\b\b100%\n")
- sys.stdout.flush()
+ if src_db is not None:
+ porttrees_data.append(TreeData(portdb.auxdb[path],
+ portdb._repo_info[path].eclass_db, path, src_db))
+
+ porttrees = [tree_data.path for tree_data in porttrees_data]
+
+ isatty = sys.stdout.isatty()
+ quiet = not isatty or '--quiet' in myopts
+ onProgress = None
+ if not quiet:
+ progressBar = portage.output.TermProgressBar()
+ progressHandler = ProgressHandler()
+ onProgress = progressHandler.onProgress
+ def display():
+ progressBar.set(progressHandler.curval, progressHandler.maxval)
+ progressHandler.display = display
+ def sigwinch_handler(signum, frame):
+ lines, progressBar.term_columns = \
+ portage.output.get_term_size()
+ signal.signal(signal.SIGWINCH, sigwinch_handler)
+
+ # Temporarily override portdb.porttrees so portdb.cp_all()
+ # will only return the relevant subset.
+ portdb_porttrees = portdb.porttrees
+ portdb.porttrees = porttrees
+ try:
+ cp_all = portdb.cp_all()
+ finally:
+ portdb.porttrees = portdb_porttrees
+
+ curval = 0
+ maxval = len(cp_all)
+ if onProgress is not None:
+ onProgress(maxval, curval)
+
+ from portage.cache.util import quiet_mirroring
+ from portage import eapi_is_supported, \
+ _validate_cache_for_unsupported_eapis
+
+ # TODO: Display error messages, but do not interfere with the progress bar.
+ # Here's how:
+ # 1) erase the progress bar
+ # 2) show the error message
+ # 3) redraw the progress bar on a new line
+ noise = quiet_mirroring()
+
+ for cp in cp_all:
+ for tree_data in porttrees_data:
+ for cpv in portdb.cp_list(cp, mytree=tree_data.path):
+ tree_data.valid_nodes.add(cpv)
+ try:
+ src = tree_data.src_db[cpv]
+ except KeyError, e:
+ noise.missing_entry(cpv)
+ del e
+ continue
+ except CacheError, ce:
+ noise.exception(cpv, ce)
+ del ce
+ continue
- if "--quiet" in myopts:
- def quicky_cpv_generator(cp_all_list):
- for x in cp_all_list:
- for y in portdb.cp_list(x):
- yield y
- source = quicky_cpv_generator(portdb.cp_all())
- noise_maker = portage.cache.util.quiet_mirroring()
- else:
- noise_maker = source = percentage_noise_maker(portdb)
- portage.cache.util.mirror_cache(source, cm, portdb.auxdb[myportdir],
- eclass_cache=ec, verbose_instance=noise_maker)
+ eapi = src.get('EAPI')
+ if not eapi:
+ eapi = '0'
+ eapi = eapi.lstrip('-')
+ eapi_supported = eapi_is_supported(eapi)
+ if not eapi_supported:
+ if not _validate_cache_for_unsupported_eapis:
+ noise.misc(cpv, "unable to validate " + \
+ "cache for EAPI='%s'" % eapi)
+ continue
+
+ dest = None
+ try:
+ dest = tree_data.dest_db[cpv]
+ except (KeyError, CacheError):
+ pass
+
+ for d in (src, dest):
+ if d is not None and d.get('EAPI') in ('', '0'):
+ del d['EAPI']
+
+ if dest is not None:
+ if not (dest['_mtime_'] == src['_mtime_'] and \
+ tree_data.eclass_db.is_eclass_data_valid(
+ dest['_eclasses_']) and \
+ set(dest['_eclasses_']) == set(src['_eclasses_'])):
+ dest = None
+ else:
+ # We don't want to skip the write unless we're really
+ # sure that the existing cache is identical, so don't
+ # trust _mtime_ and _eclasses_ alone.
+ for k in set(chain(src, dest)).difference(
+ ('_mtime_', '_eclasses_')):
+ if dest.get(k, '') != src.get(k, ''):
+ dest = None
+ break
+
+ if dest is not None:
+ # The existing data is valid and identical,
+ # so there's no need to overwrite it.
+ continue
+
+ try:
+ inherited = src.get('INHERITED', '')
+ eclasses = src.get('_eclasses_')
+ except CacheError, ce:
+ noise.exception(cpv, ce)
+ del ce
+ continue
+
+ if eclasses is not None:
+ if not tree_data.eclass_db.is_eclass_data_valid(
+ src['_eclasses_']):
+ noise.eclass_stale(cpv)
+ continue
+ inherited = eclasses
+ else:
+ inherited = inherited.split()
+
+ if inherited:
+ if tree_data.src_db.complete_eclass_entries and \
+ eclasses is None:
+ noise.corruption(cpv, "missing _eclasses_ field")
+ continue
+
+ # Even if _eclasses_ already exists, replace it with data from
+ # eclass_cache, in order to insert local eclass paths.
+ try:
+ eclasses = tree_data.eclass_db.get_eclass_data(inherited)
+ except KeyError:
+ # INHERITED contains a non-existent eclass.
+ noise.eclass_stale(cpv)
+ continue
+
+ if eclasses is None:
+ noise.eclass_stale(cpv)
+ continue
+ src['_eclasses_'] = eclasses
+
+ if not eapi_supported:
+ src = {
+ 'EAPI' : '-' + eapi,
+ '_mtime_' : src['_mtime_'],
+ '_eclasses_' : src['_eclasses_'],
+ }
+
+ try:
+ tree_data.dest_db[cpv] = src
+ except CacheError, ce:
+ noise.exception(cpv, ce)
+ del ce
+
+ curval += 1
+ if onProgress is not None:
+ onProgress(maxval, curval)
+
+ if onProgress is not None:
+ onProgress(maxval, curval)
+
+ for tree_data in porttrees_data:
+ try:
+ dead_nodes = set(tree_data.dest_db.iterkeys())
+ except CacheError, e:
+ writemsg_level("Error listing cache entries for " + \
+ "'%s': %s, continuing...\n" % (tree_data.path, e),
+ level=logging.ERROR, noiselevel=-1)
+ del e
+ else:
+ dead_nodes.difference_update(tree_data.valid_nodes)
+ for cpv in dead_nodes:
+ try:
+ tree_data.dest_db[cpv]
+ except (KeyError, CacheError):
+ pass
+
+ if not quiet:
+ # make sure the final progress is displayed
+ progressHandler.display()
+ print
+ signal.signal(signal.SIGWINCH, signal.SIG_DFL)
sys.stdout.flush()
os.umask(old_umask)
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):
+ root_config = trees[settings['ROOT']]['root_config']
+ world_set = 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
+
+ vardb = root_config.trees['vartree'].dbapi
+ expanded_atoms = set(atoms)
+ from portage.dep import Atom
+ for atom in atoms:
+ for cpv in vardb.match(atom):
+ slot, = vardb.aux_get(cpv, ['SLOT'])
+ if not slot:
+ slot = '0'
+ expanded_atoms.add(Atom('%s:%s' % (portage.cpv_getkey(cpv), slot)))
+
+ pretend = '--pretend' in opts
+ locked = False
+ if not pretend and hasattr(world_set, 'lock'):
+ world_set.lock()
+ locked = True
+ try:
+ discard_atoms = set()
+ world_set.load()
+ for atom in world_set:
+ if not isinstance(atom, Atom):
+ # nested set
+ continue
+ for arg_atom in expanded_atoms:
+ if arg_atom.intersects(atom) and \
+ not (arg_atom.slot and not atom.slot):
+ 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))
+
+ if '--ask' in opts:
+ prompt = "Would you like to remove these " + \
+ "packages from your world favorites?"
+ if userquery(prompt) == 'No':
+ return os.EX_OK
+
+ remaining = set(world_set)
+ remaining.difference_update(discard_atoms)
+ if not pretend:
+ 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
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 = {}
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()
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:
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
"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 " + \
myoptions, myargs = parser.parse_args(args=tmpcmdline)
+ if myoptions.deselect == "True":
+ myoptions.deselect = True
+
if myoptions.root_deps == "True":
myoptions.root_deps = True
sys.exit(1)
myaction = action_opt
+ if myaction is None and myoptions.deselect is True:
+ myaction = 'deselect'
+
myfiles += myargs
return myaction, myopts, myfiles
if portage.secpass < 2:
# We've already allowed "--version" and "--help" above.
if "--pretend" not in myopts and myaction not in ("search","info"):
- need_superuser = not \
+ need_superuser = myaction in ('deselect',) or not \
(fetchonly or \
(buildpkgonly and secpass >= 1) or \
myaction in ("metadata", "regen") or \
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