From 5b7bd9e10100e3d297c223cd9471bfc850f50f5c Mon Sep 17 00:00:00 2001 From: Zac Medico Date: Tue, 3 Jul 2007 01:24:54 +0000 Subject: [PATCH] Clean up system and world set handling. For forward compatibility, the world file handling now allows the world file to contain arbitrary atoms such as slot atoms. (trunk r7130:7133) svn path=/main/branches/2.1.2/; revision=7134 --- bin/emaint | 3 +- bin/emerge | 342 +++++++++++++++++++++++++++++++++++------------------ 2 files changed, 232 insertions(+), 113 deletions(-) diff --git a/bin/emaint b/bin/emaint index 0353a52ce..90809ee4f 100755 --- a/bin/emaint +++ b/bin/emaint @@ -35,11 +35,12 @@ class WorldHandler(object): if not portage.isvalidatom(atom): self.invalid.append(atom) continue + cp = portage.dep_getkey(atom) okay = True if not vardb.match(atom): self.not_installed.append(atom) okay = False - if portage.catsplit(atom)[0] not in categories: + if portage.catsplit(cp)[0] not in categories: self.invalid_category.append(atom) okay = False if okay: diff --git a/bin/emerge b/bin/emerge index dcfaa3d8e..58c53d4cd 100755 --- a/bin/emerge +++ b/bin/emerge @@ -567,8 +567,10 @@ def getlist(settings, mode): def clean_world(vardb, cpv): """Remove a package from the world file when unmerged.""" - world_filename = os.path.join(vardb.root, portage.WORLD_FILE) - worldlist = portage_util.grabfile(world_filename) + world_set = WorldSet(vardb.settings) + world_set.lock() + world_set.load() + worldlist = list(world_set) mykey = portage.cpv_getkey(cpv) newworldlist = [] for x in worldlist: @@ -586,18 +588,90 @@ def clean_world(vardb, cpv): else: #this doesn't match the package we're unmerging; keep it. newworldlist.append(x) - - newworldlist.sort() - - portage_util.ensure_dirs(os.path.join(vardb.root, portage.PRIVATE_PATH), - gid=portage.portage_gid, mode=02770) - portage_util.write_atomic(world_filename, "\n".join(newworldlist)+"\n") -def genericdict(mylist): - mynewdict={} - for x in mylist: - mynewdict[portage.dep_getkey(x)]=x - return mynewdict + world_set.clear() + world_set.update(newworldlist) + world_set.save() + world_set.unlock() + +class AtomSet(object): + def __init__(self, atoms=None): + self._atoms = {} + if atoms: + self.update(atoms) + def clear(self): + self._atoms.clear() + def add(self, atom): + cp = portage.dep_getkey(atom) + cp_list = self._atoms.get(cp) + if cp_list is None: + cp_list = [] + self._atoms[cp] = cp_list + if atom not in cp_list: + cp_list.append(atom) + def update(self, atoms): + for atom in atoms: + self.add(atom) + def __contains__(self, atom): + cp = portage.dep_getkey(atom) + if cp in self._atoms and atom in self._atoms[cp]: + return True + return False + def findAtomForPackage(self, cpv, metadata): + """Return the best match for a given package from the arguments, or + None if there are no matches. This matches virtual arguments against + the PROVIDE metadata. This can raise an InvalidDependString exception + if an error occurs while parsing PROVIDE.""" + cpv_slot = "%s:%s" % (cpv, metadata["SLOT"]) + cp = portage.dep_getkey(cpv) + atoms = self._atoms.get(cp) + if atoms: + best_match = portage.best_match_to_list(cpv_slot, atoms) + if best_match: + return best_match + provides = portage.flatten(portage_dep.use_reduce( + portage_dep.paren_reduce(metadata["PROVIDE"]), + uselist=metadata["USE"].split())) + for provide in provides: + provided_cp = portage.dep_getkey(provide) + atoms = self._atoms.get(provided_cp) + if atoms: + transformed_atoms = [atom.replace(provided_cp, cp) for atom in atoms] + best_match = portage.best_match_to_list(cpv_slot, transformed_atoms) + if best_match: + return atoms[transformed_atoms.index(best_match)] + return None + def __iter__(self): + for atoms in self._atoms.itervalues(): + for atom in atoms: + yield atom + +class SystemSet(AtomSet): + def __init__(self, settings, **kwargs): + AtomSet.__init__(self, **kwargs) + self.update(getlist(settings, "system")) + +class WorldSet(AtomSet): + def __init__(self, settings, **kwargs): + AtomSet.__init__(self, **kwargs) + self.world_file = os.path.join(settings["ROOT"], portage.WORLD_FILE) + self._lock = None + def _ensure_dirs(self): + portage_util.ensure_dirs(os.path.dirname(self.world_file), + gid=portage.portage_gid, mode=02750, mask=02) + def load(self): + self.clear() + self.update(portage_util.grabfile_package(self.world_file)) + def save(self): + self._ensure_dirs() + portage.write_atomic(self.world_file, + "\n".join(sorted(self)) + "\n") + def lock(self): + self._ensure_dirs() + self._lock = portage_locks.lockfile(self.world_file, wantnewlockfile=1) + def unlock(self): + portage_locks.unlockfile(self._lock) + self._lock = None def filter_iuse_defaults(iuse): for flag in iuse: @@ -986,7 +1060,7 @@ class depgraph: self._parent_child_digraph = digraph() self.orderedkeys=[] self.outdatedpackages=[] - self._args_atoms = {} + self._args_atoms = AtomSet() self._args_nodes = set() self._sets = {} self._sets_nodes = {} @@ -1129,7 +1203,7 @@ class depgraph: if not arg and myroot == self.target_root: try: - arg = self._find_atom_for_pkg(self._args_atoms, mykey, metadata) + arg = self._args_atoms.findAtomForPackage(mykey, metadata) except portage_exception.InvalidDependString, e: if mytype != "installed": show_invalid_depstring_notice(tuple(mybigkey+["merge"]), @@ -1241,8 +1315,8 @@ class depgraph: if arg: self._args_nodes.add(jbigkey) try: - for set_name, pkg_set in self._sets.iteritems(): - atom = self._find_atom_for_pkg(pkg_set, mykey, metadata) + for set_name, atom_set in self._sets.iteritems(): + atom = atom_set.findAtomForPackage(mykey, metadata) if atom: self._sets_nodes[set_name].add(jbigkey) except portage_exception.InvalidDependString, e: @@ -1370,7 +1444,7 @@ class depgraph: None, "--onlydeps" not in self.myopts): return (0,myfavorites) elif not "--oneshot" in self.myopts: - myfavorites.append(mykey) + myfavorites.append("="+mykey) elif ext==".ebuild": x = os.path.realpath(x) mykey=os.path.basename(os.path.normpath(x+"/../.."))+"/"+os.path.splitext(os.path.basename(x))[0] @@ -1393,7 +1467,7 @@ class depgraph: None, "--onlydeps" not in self.myopts): return (0,myfavorites) elif not "--oneshot" in self.myopts: - myfavorites.append(mykey) + myfavorites.append("="+mykey) else: if not is_valid_package_atom(x): portage.writemsg("\n\n!!! '%s' is not a valid package atom.\n" % x, @@ -1408,12 +1482,16 @@ class depgraph: settings=pkgsettings) if (mykey and not mykey.startswith("null/")) or \ "--usepkgonly" in self.myopts: + if "--oneshot" not in self.myopts: + myfavorites.append(mykey) arg_atoms.append((x, mykey)) continue mykey = portage.dep_expand(x, mydb=portdb, settings=pkgsettings) arg_atoms.append((x, mykey)) + if "--oneshot" not in self.myopts: + myfavorites.append(mykey) except ValueError, errpkgs: print "\n\n!!! The short ebuild name \"" + x + "\" is ambiguous. Please specify" print "!!! one of the following fully-qualified ebuild names instead:\n" @@ -1474,8 +1552,7 @@ class depgraph: """ These are used inside self.create() in order to ensure packages that happen to match arguments are not incorrectly marked as nomerge.""" for myarg, myatom in arg_atoms: - self._args_atoms.setdefault( - portage.dep_getkey(myatom), []).append(myatom) + self._args_atoms.add(myatom) for myarg, myatom in arg_atoms: try: self.mysd = self.select_dep(myroot, myatom, arg=myarg) @@ -1502,10 +1579,6 @@ class depgraph: if not self.mysd: return (0,myfavorites) - elif not "--oneshot" in self.myopts: - mykey = portage.dep_getkey(myatom) - if mykey not in myfavorites: - myfavorites.append(mykey) missing=0 if "--usepkgonly" in self.myopts: @@ -1582,8 +1655,7 @@ class depgraph: if not mymerge and arg: # A provided package has been specified on the command line. The # package will not be merged and a warning will be displayed. - cp = portage.dep_getkey(depstring) - if cp in self._args_atoms and depstring in self._args_atoms[cp]: + if depstring in self._args_atoms: self._pprovided_args.append((arg, depstring)) if myparent: @@ -2268,27 +2340,25 @@ class depgraph: matches = portdb.gvisible(portdb.visible(mylist)) return [x for x in mylist \ if x in matches or not portdb.cpv_exists(x)] - def create_cp_dict(atom_list): - cp_dict = {} - for atom in atom_list: - cp_dict.setdefault(portage.dep_getkey(atom), []).append(atom) - return cp_dict world_problems = False if mode=="system": - mylist = getlist(self.settings, "system") - self._sets["system"] = create_cp_dict(mylist) + system_set = SystemSet(self.settings) + mylist = list(system_set) + self._sets["system"] = system_set self._sets_nodes["system"] = set() else: #world mode - worldlist = getlist(self.settings, "world") - self._sets["world"] = create_cp_dict(worldlist) + world_set = WorldSet(self.settings) + world_set.load() + worldlist = list(world_set) + self._sets["world"] = world_set self._sets_nodes["world"] = set() - mylist = getlist(self.settings, "system") - self._sets["system"] = create_cp_dict(mylist) + system_set = SystemSet(self.settings) + mylist = list(system_set) + self._sets["system"] = system_set self._sets_nodes["system"] = set() - worlddict=genericdict(worldlist) - for x in worlddict: + for x in worldlist: if not portage.isvalidatom(x): world_problems = True continue @@ -2358,8 +2428,7 @@ class depgraph: mylist = newlist for myatom in mylist: - self._args_atoms.setdefault( - portage.dep_getkey(myatom), []).append(myatom) + self._args_atoms.add(myatom) missing_atoms = [] for mydep in mylist: @@ -2386,31 +2455,6 @@ class depgraph: return 1 - def _find_atom_for_pkg(self, pkg_set, cpv, metadata): - """Return the best match for a given package from the arguments, or - None if there are no matches. This matches virtual arguments against - the PROVIDE metadata. This can raise an InvalidDependString exception - if an error occurs while parsing PROVIDE.""" - cpv_slot = "%s:%s" % (cpv, metadata["SLOT"]) - cp = portage.dep_getkey(cpv) - atoms = pkg_set.get(cp) - if atoms: - best_match = portage.best_match_to_list(cpv_slot, atoms) - if best_match: - return best_match - provides = portage.flatten(portage_dep.use_reduce( - portage_dep.paren_reduce(metadata["PROVIDE"]), - uselist=metadata["USE"].split())) - for provide in provides: - provided_cp = portage.dep_getkey(provide) - atoms = pkg_set.get(provided_cp) - if atoms: - transformed_atoms = [atom.replace(provided_cp, cp) for atom in atoms] - best_match = portage.best_match_to_list(cpv_slot, transformed_atoms) - if best_match: - return atoms[transformed_atoms.index(best_match)] - return None - def display(self,mylist,verbosity=None): if verbosity is None: verbosity = ("--quiet" in self.myopts and 1 or \ @@ -2984,8 +3028,8 @@ class depgraph: arg, atom = arg_atom arg_refs[arg_atom] = [] cp = portage.dep_getkey(atom) - for set_name, pkg_set in self._sets.iteritems(): - if cp in pkg_set and atom in pkg_set[cp]: + for set_name, atom_set in self._sets.iteritems(): + if atom in atom_set: arg_refs[arg_atom].append(set_name) msg = [] msg.append(bad("\nWARNING: ")) @@ -3067,8 +3111,64 @@ class depgraph: if release.endswith('-r0'): release = release[:-3] - def outdated(self): - return self.outdatedpackages + def saveNomergeFavorites(self, mergelist, favorites): + """Find atoms in favorites that are not in the mergelist and add them + to the world file if necessary.""" + for x in ("--onlydeps", "--pretend", "--fetchonly", "--fetch-all-uri"): + if x in self.myopts: + return + favorites_set = AtomSet(favorites) + system_set = SystemSet(self.settings) + world_set = WorldSet(self.settings) + world_set.lock() + world_set.load() + merge_atoms = set() + atom_pkgs = {} + for x in mergelist: + pkg_type = x[0] + if pkg_type not in self.pkg_tree_map: + continue + pkg_type, root, pkg_key, pkg_status = x + if root != self.target_root: + continue + metadata = dict(izip(self._mydbapi_keys, + self.mydbapi[root].aux_get(pkg_key, self._mydbapi_keys))) + try: + atom = favorites_set.findAtomForPackage(pkg_key, metadata) + except portage_exception.InvalidDependString, e: + writemsg("\n\n!!! '%s' has invalid PROVIDE: %s\n" % \ + (pkg_key, str(e)), noiselevel=-1) + writemsg("!!! see '%s'\n\n" % os.path.join( + root, portage.VDB_PATH, pkg_key, "PROVIDE"), noiselevel=-1) + del e + if atom: + merge_atoms.add(atom) + added_favorites = set() + root = self.target_root + for atom in set(favorites_set).difference(merge_atoms): + pkgs = self.mydbapi[root].match(atom) + for pkg_key in pkgs: + metadata = dict(izip(self._mydbapi_keys, + self.mydbapi[root].aux_get(pkg_key, self._mydbapi_keys))) + try: + if not (system_set.findAtomForPackage(pkg_key, metadata) or \ + world_set.findAtomForPackage(pkg_key, metadata)): + myfavkey = portage.cpv_getkey(pkg_key) + if myfavkey in added_favorites: + continue + added_favorites.add(myfavkey) + world_set.add(myfavkey) + modified = True + print ">>> Recording",myfavkey,"in \"world\" favorites file..." + except portage_exception.InvalidDependString, e: + writemsg("\n\n!!! '%s' has invalid PROVIDE: %s\n" % \ + (pkg_key, str(e)), noiselevel=-1) + writemsg("!!! see '%s'\n\n" % os.path.join( + root, portage.VDB_PATH, pkg_key, "PROVIDE"), noiselevel=-1) + del e + if added_favorites: + world_set.save() + world_set.unlock() class PackageCounters(object): @@ -3148,6 +3248,7 @@ class MergeTask(object): failed_fetches = [] fetchonly = "--fetchonly" in self.myopts or \ "--fetch-all-uri" in self.myopts + pretend = "--pretend" in self.myopts mymergelist=[] ldpath_mtimes = mtimedb["ldpath"] xterm_titles = "notitles" not in self.settings.features @@ -3206,34 +3307,11 @@ class MergeTask(object): del shown_verifying_msg, quiet_settings #buildsyspkg: I need mysysdict also on resume (moved from the else block) - mysysdict = genericdict(getlist(self.settings, "system")) + system_set = SystemSet(self.settings) + favorites_set = AtomSet(favorites) + world_set = WorldSet(self.settings) if "--resume" not in self.myopts: - myfavs = portage.grabfile( - os.path.join(self.target_root, portage.WORLD_FILE)) - myfavdict=genericdict(myfavs) - for x in range(len(mylist)): - if mylist[x][3]!="nomerge": - # Add to the mergelist - mymergelist.append(mylist[x]) - else: - myfavkey=portage.cpv_getkey(mylist[x][2]) - if "--onlydeps" in self.myopts: - continue - # Add to the world file. Since we won't be able to later. - if "--fetchonly" not in self.myopts and \ - myfavkey in favorites: - #don't record if already in system profile or already recorded - if (not mysysdict.has_key(myfavkey)) and (not myfavdict.has_key(myfavkey)): - #we don't have a favorites entry for this package yet; add one - myfavdict[myfavkey]=myfavkey - print ">>> Recording",myfavkey,"in \"world\" favorites file..." - if not ("--fetchonly" in self.myopts or \ - "--fetch-all-uri" in self.myopts or \ - "--pretend" in self.myopts): - portage.write_atomic( - os.path.join(self.target_root, portage.WORLD_FILE), - "\n".join(sorted(myfavdict.values())) + "\n") - + mymergelist = mylist mtimedb["resume"]["mergelist"]=mymergelist[:] mtimedb.commit() @@ -3278,9 +3356,13 @@ class MergeTask(object): del fetch_log, logfile, fd_pipes, fetch_env, fetch_args, \ resume_opts + metadata_keys = [k for k in portage.auxdbkeys \ + if not k.startswith("UNUSED_")] + ["USE"] + mergecount=0 for x in mymergelist: mergecount+=1 + pkg_type = x[0] myroot=x[1] pkg_key = x[2] pkgindex=2 @@ -3288,6 +3370,22 @@ class MergeTask(object): bindb = self.trees[myroot]["bintree"].dbapi vartree = self.trees[myroot]["vartree"] pkgsettings = self.pkgsettings[myroot] + metadata = {} + if pkg_type == "blocks": + pass + elif pkg_type == "ebuild": + mydbapi = portdb + metadata.update(izip(metadata_keys, + mydbapi.aux_get(pkg_key, metadata_keys))) + pkgsettings.setcpv(pkg_key, mydb=mydbapi) + metadata["USE"] = pkgsettings["USE"] + else: + if pkg_type == "binary": + mydbapi = bindb + else: + raise AssertionError("Package type: '%s'" % pkg_type) + metadata.update(izip(metadata_keys, + mydbapi.aux_get(pkg_key, metadata_keys))) if x[0]=="blocks": pkgindex=3 y = portdb.findname(pkg_key) @@ -3491,24 +3589,23 @@ class MergeTask(object): #need to check for errors if "--buildpkgonly" not in self.myopts: self.trees[x[1]]["vartree"].inject(x[2]) - myfavkey=portage.cpv_getkey(x[2]) - if "--fetchonly" not in self.myopts and \ - "--fetch-all-uri" not in self.myopts and \ - myfavkey in favorites: - myfavs = portage.grabfile(os.path.join(myroot, portage.WORLD_FILE)) - myfavdict=genericdict(myfavs) + myfavkey = portage.cpv_getkey(x[2]) + if not fetchonly and not pretend and \ + favorites_set.findAtomForPackage(pkg_key, metadata): + world_set.lock() + world_set.load() #don't record if already in system profile or already recorded - if (not mysysdict.has_key(myfavkey)) and (not myfavdict.has_key(myfavkey)): + if not system_set.findAtomForPackage(pkg_key, metadata) and \ + not world_set.findAtomForPackage(pkg_key, metadata): #we don't have a favorites entry for this package yet; add one - myfavdict[myfavkey]=myfavkey + world_set.add(myfavkey) print ">>> Recording",myfavkey,"in \"world\" favorites file..." emergelog(xterm_titles, " === ("+\ str(mergecount)+" of "+\ str(len(mymergelist))+\ ") Updating world file ("+x[pkgindex]+")") - portage.write_atomic( - os.path.join(myroot, portage.WORLD_FILE), - "\n".join(sorted(myfavdict.values()))+"\n") + world_set.save() + world_set.unlock() if "--pretend" not in self.myopts and \ "--fetchonly" not in self.myopts and \ @@ -4930,8 +5027,8 @@ def action_depclean(settings, trees, ldpath_mtimes, dep_check_trees[myroot]["porttree"] = dep_check_trees[myroot]["vartree"] syslist = getlist(settings, "system") worldlist = getlist(settings, "world") - system_world_dict = genericdict(worldlist) - system_world_dict.update(genericdict(syslist)) + system_and_world = AtomSet(syslist) + system_and_world.update(worldlist) fakedb = portage.fakedbapi(settings=settings) myvarlist = vardb.cpv_all() @@ -4960,6 +5057,7 @@ def action_depclean(settings, trees, ldpath_mtimes, remaining_atoms += [(atom, 'system', hard) for atom in syslist if vardb.match(atom)] unresolveable = {} aux_keys = ["DEPEND", "RDEPEND", "PDEPEND"] + metadata_keys = ["PROVIDE", "SLOT", "USE"] while remaining_atoms: atom, parent, priority = remaining_atoms.pop() @@ -4968,7 +5066,25 @@ def action_depclean(settings, trees, ldpath_mtimes, if not atom.startswith("!") and priority == hard: unresolveable.setdefault(atom, []).append(parent) continue - if portage.dep_getkey(atom) not in system_world_dict: + system_or_world = False + if parent in ('world', 'system'): + system_or_world = True + else: + for pkg in pkgs: + metadata = dict(izip(metadata_keys, + vardb.aux_get(pkg, metadata_keys))) + try: + if system_and_world.findAtomForPackage(pkg, metadata): + system_or_world = True + break + except portage_exception.InvalidDependString, e: + writemsg("\n\n!!! '%s' has invalid PROVIDE: %s\n" % \ + (pkg, str(e)), noiselevel=-1) + writemsg("!!! see '%s'\n\n" % os.path.join( + myroot, portage.VDB_PATH, pkg, "PROVIDE"), + noiselevel=-1) + del e + if not system_or_world: # Prune all but the best matching slot, since that's all that a # deep world update would pull in. Don't prune if the cpv is in # system or world though, since those sets trigger greedy update @@ -5281,6 +5397,8 @@ def action_build(settings, trees, mtimedb, pkglist.append(pkg) else: pkglist = mydepgraph.altlist() + if favorites: + mydepgraph.saveNomergeFavorites(pkglist, favorites) del mydepgraph mergetask = MergeTask(settings, trees, myopts) retval = mergetask.merge(pkglist, favorites, mtimedb) -- 2.26.2