#!/usr/bin/env python # Copyright 1999-2007 Gentoo Foundation # Distributed under the terms of the GNU General Public License v2 # # Zac Medico # import os, sys try: from portage.versions import best, pkgcmp, pkgsplit from portage.dep import dep_getkey from portage.const import INCREMENTALS except ImportError: # backward compat for <=portage-2.1.2 from portage_versions import best, pkgcmp, pkgsplit from portage_dep import dep_getkey from portage_const import INCREMENTALS def cmp_pkgs(cpv1, cpv2): return pkgcmp(pkgsplit(cpv2), pkgsplit(cpv1)) def iter_config_instances(profile_paths): import portage from portage import normalize_path portdir = portage.settings["PORTDIR"] for profile_path in profile_paths: myoutput = [] myoutput.append(profile_path) profdir = os.path.join(portdir, "profiles", profile_path) settings = portage.config( config_profile_path=profdir, config_incrementals=INCREMENTALS, local_config=False) parents = portage.grabfile(os.path.join(profdir, "parent")) parents = [normalize_path(os.path.join(profile_path, parent)) \ for parent in parents] yield profile_path, parents, settings def walk_profiles_dir(): import portage from portage import normalize_path mygraph = portage.digraph() portdir = portage.settings["PORTDIR"] profiles_dir = os.path.join(portdir, "profiles") profile_indicators = set(["deprecated", "make.defaults", "packages", "packages.build", "package.provided", "package.use", "package.use.force", "package.use.mask", "parent", "profile.bashrc", "use.force", "use.mask", "virtuals"]) def add_recursive(current_dir): profile_path = current_dir[len(profiles_dir)+1:] if mygraph.contains(profile_path): return mygraph.add(profile_path, None) for parent in portage.grabfile( os.path.join(current_dir, "parent")): parent_path = normalize_path( os.path.join(profile_path, parent)) mygraph.add(profile_path, parent_path) add_recursive(os.path.join(profiles_dir, parent_path)) for current_dir, dirs, files in os.walk(profiles_dir): if profile_indicators.intersection(files): add_recursive(current_dir) return mygraph def grab_profile_stack(mygraph, current_node, selected_nodes): """Order nodes from highest to lowest overall reference count for optimal leaf node selection.""" if current_node in selected_nodes: return selected_nodes.add(current_node) for child in mygraph.child_nodes(current_node): grab_profile_stack(mygraph, child, selected_nodes) for parent in mygraph.parent_nodes(current_node): grab_profile_stack(mygraph, parent, selected_nodes) def list_profile_stack(mygraph, selected_nodes): tempgraph = mygraph.copy() for node in set(tempgraph.order).difference(selected_nodes): tempgraph.remove(node) profile_stack = [] while not tempgraph.empty(): leaf_nodes = tempgraph.leaf_nodes() if leaf_nodes: node = leaf_nodes[0] else: print "Circular Relationship" tempgraph.debug_print() raise Exception("Circular Relationship") tempgraph.remove(node) profile_stack.append(node) profile_stack.reverse() return profile_stack def find_missing_masks(settings): from portage import portdb maskdict = settings.pmaskdict upgrades = [] for orig_atom in settings.packages: atom = orig_atom if atom.startswith("*"): atom = atom[1:] matches = portdb.xmatch("match-all", atom) best_match = None slot_atom = None if not matches: continue best_match = best(matches) cp = dep_getkey(atom) cp_matches = portdb.xmatch("match-all", cp) cp_matches.sort(cmp_pkgs) best_match_index = cp_matches.index(best_match) if best_match_index == 0: continue need_mask = cp_matches[:best_match_index] masks = maskdict.get(cp) if masks: masked = set() for x in masks: masked.update(portdb.xmatch("match-all", x)) if not masked.difference(need_mask): continue upgrades.append(orig_atom) return upgrades def check_profiles(): header = [] header.append("Profile") header.append("Atoms") print "\t".join(header) print mygraph = walk_profiles_dir() root_nodes = mygraph.root_nodes() root_nodes.sort() missing_masks = {} for node in root_nodes: selected_nodes = set() grab_profile_stack(mygraph, node, selected_nodes) profile_stack = list_profile_stack(mygraph, selected_nodes) for profile_path, parents, settings in iter_config_instances(profile_stack): missing = set(find_missing_masks(settings)) missing_masks[profile_path] = missing if not missing: continue missing = missing.copy() for p in parents: missing -= missing_masks[p] if not missing: continue missing = list(missing) missing.sort() myoutput = [] myoutput.append(profile_path) myoutput.append(" ".join(missing)) print "\t".join(myoutput) for node in profile_stack: mygraph.remove(node) if __name__ == "__main__": check_profiles()