diff options
Diffstat (limited to 'pym/gentoolkit/revdep_rebuild/assign.py')
-rw-r--r-- | pym/gentoolkit/revdep_rebuild/assign.py | 312 |
1 files changed, 164 insertions, 148 deletions
diff --git a/pym/gentoolkit/revdep_rebuild/assign.py b/pym/gentoolkit/revdep_rebuild/assign.py index 570c114..bfc56eb 100644 --- a/pym/gentoolkit/revdep_rebuild/assign.py +++ b/pym/gentoolkit/revdep_rebuild/assign.py @@ -9,6 +9,7 @@ import os import io import re import time + current_milli_time = lambda: int(round(time.time() * 1000)) import portage @@ -17,160 +18,175 @@ from portage.output import bold, red, yellow, green class _file_matcher: - """ - Compares files by basename and parent directory (device, inode), - so comparisons work regardless of directory symlinks. If a - parent directory does not exist, the realpath of the parent - directory is used instead of the (device, inode). When multiple - files share the same parent directory, stat is only called - once per directory, and the result is cached internally. - """ - def __init__(self): - self._file_ids = {} - self._added = {} - - def _file_id(self, filename): - try: - return self._file_ids[filename] - except KeyError: - try: - st = os.stat(filename) - except OSError as e: - if e.errno != errno.ENOENT: - raise - file_id = (os.path.realpath(filename),) - else: - file_id = (st.st_dev, st.st_ino) - - self._file_ids[filename] = file_id - return file_id - - def _file_key(self, filename): - head, tail = os.path.split(filename) - key = self._file_id(head) + (tail,) - return key - - def add(self, filename): - self._added[self._file_key(filename)] = filename - - def intersection(self, other): - for file_key in self._added: - match = other._added.get(file_key) - if match is not None: - yield match + """ + Compares files by basename and parent directory (device, inode), + so comparisons work regardless of directory symlinks. If a + parent directory does not exist, the realpath of the parent + directory is used instead of the (device, inode). When multiple + files share the same parent directory, stat is only called + once per directory, and the result is cached internally. + """ + + def __init__(self): + self._file_ids = {} + self._added = {} + + def _file_id(self, filename): + try: + return self._file_ids[filename] + except KeyError: + try: + st = os.stat(filename) + except OSError as e: + if e.errno != errno.ENOENT: + raise + file_id = (os.path.realpath(filename),) + else: + file_id = (st.st_dev, st.st_ino) + + self._file_ids[filename] = file_id + return file_id + + def _file_key(self, filename): + head, tail = os.path.split(filename) + key = self._file_id(head) + (tail,) + return key + + def add(self, filename): + self._added[self._file_key(filename)] = filename + + def intersection(self, other): + for file_key in self._added: + match = other._added.get(file_key) + if match is not None: + yield match def assign_packages(broken, logger, settings): - ''' Finds and returns packages that owns files placed in broken. - Broken is list of files - ''' - stime = current_milli_time() - - broken_matcher = _file_matcher() - for filename in broken: - broken_matcher.add(filename) - - assigned_pkgs = set() - assigned_filenames = set() - for group in os.listdir(settings['PKG_DIR']): - grppath = settings['PKG_DIR'] + group - if not os.path.isdir(grppath): - continue - for pkg in os.listdir(grppath): - pkgpath = settings['PKG_DIR'] + group + '/' + pkg - if not os.path.isdir(pkgpath): - continue - f = pkgpath + '/CONTENTS' - if os.path.exists(f): - contents_matcher = _file_matcher() - try: - with io.open(f, 'r', encoding='utf_8') as cnt: - for line in cnt.readlines(): - m = re.match(r'^obj (/[^ ]+)', line) - if m is not None: - contents_matcher.add(m.group(1)) - except Exception as e: - logger.warning(red(' !! Failed to read ' + f)) - logger.warning(red(' !! Error was:' + str(e))) - else: - for m in contents_matcher.intersection(broken_matcher): - found = group+'/'+pkg - assigned_pkgs.add(found) - assigned_filenames.add(m) - logger.info('\t' + green('* ') + m + - ' -> ' + bold(found)) - - broken_filenames = set(broken) - orphaned = broken_filenames.difference(assigned_filenames) - ftime = current_milli_time() - logger.debug("\tassign_packages(); assigned " - "%d packages, %d orphans in %d milliseconds" - % (len(assigned_pkgs), len(orphaned), ftime-stime)) - - return (assigned_pkgs, orphaned) + """Finds and returns packages that owns files placed in broken. + Broken is list of files + """ + stime = current_milli_time() + + broken_matcher = _file_matcher() + for filename in broken: + broken_matcher.add(filename) + + assigned_pkgs = set() + assigned_filenames = set() + for group in os.listdir(settings["PKG_DIR"]): + grppath = settings["PKG_DIR"] + group + if not os.path.isdir(grppath): + continue + for pkg in os.listdir(grppath): + pkgpath = settings["PKG_DIR"] + group + "/" + pkg + if not os.path.isdir(pkgpath): + continue + f = pkgpath + "/CONTENTS" + if os.path.exists(f): + contents_matcher = _file_matcher() + try: + with io.open(f, "r", encoding="utf_8") as cnt: + for line in cnt.readlines(): + m = re.match(r"^obj (/[^ ]+)", line) + if m is not None: + contents_matcher.add(m.group(1)) + except Exception as e: + logger.warning(red(" !! Failed to read " + f)) + logger.warning(red(" !! Error was:" + str(e))) + else: + for m in contents_matcher.intersection(broken_matcher): + found = group + "/" + pkg + assigned_pkgs.add(found) + assigned_filenames.add(m) + logger.info("\t" + green("* ") + m + " -> " + bold(found)) + + broken_filenames = set(broken) + orphaned = broken_filenames.difference(assigned_filenames) + ftime = current_milli_time() + logger.debug( + "\tassign_packages(); assigned " + "%d packages, %d orphans in %d milliseconds" + % (len(assigned_pkgs), len(orphaned), ftime - stime) + ) + + return (assigned_pkgs, orphaned) def get_best_match(cpv, cp, logger): - """Tries to find another version of the pkg with the same slot - as the deprecated installed version. Failing that attempt to get any version - of the same app - - @param cpv: string - @param cp: string - @rtype tuple: ([cpv,...], SLOT) - """ - - slot = portage.db[portage.root]["vartree"].dbapi.aux_get(cpv, ["SLOT"])[0] - logger.warning('\t%s "%s" %s.' % (yellow('* Warning:'), cpv,bold('ebuild not found.'))) - logger.debug('\tget_best_match(); Looking for %s:%s' %(cp, slot)) - try: - match = portdb.match('%s:%s' %(cp, slot)) - except portage.exception.InvalidAtom: - match = None - - if not match: - logger.warning('\t' + red('!!') + ' ' + yellow( - 'Could not find ebuild for %s:%s' %(cp, slot))) - slot = [''] - match = portdb.match(cp) - if not match: - logger.warning('\t' + red('!!') + ' ' + - yellow('Could not find ebuild for ' + cp)) - return match, slot + """Tries to find another version of the pkg with the same slot + as the deprecated installed version. Failing that attempt to get any version + of the same app + + @param cpv: string + @param cp: string + @rtype tuple: ([cpv,...], SLOT) + """ + + slot = portage.db[portage.root]["vartree"].dbapi.aux_get(cpv, ["SLOT"])[0] + logger.warning( + '\t%s "%s" %s.' % (yellow("* Warning:"), cpv, bold("ebuild not found.")) + ) + logger.debug("\tget_best_match(); Looking for %s:%s" % (cp, slot)) + try: + match = portdb.match("%s:%s" % (cp, slot)) + except portage.exception.InvalidAtom: + match = None + + if not match: + logger.warning( + "\t" + + red("!!") + + " " + + yellow("Could not find ebuild for %s:%s" % (cp, slot)) + ) + slot = [""] + match = portdb.match(cp) + if not match: + logger.warning( + "\t" + red("!!") + " " + yellow("Could not find ebuild for " + cp) + ) + return match, slot def get_slotted_cps(cpvs, logger): - """Uses portage to reduce the cpv list into a cp:slot list and returns it - """ - from portage.versions import catpkgsplit - from portage import portdb - - cps = [] - for cpv in cpvs: - parts = catpkgsplit(cpv) - if not parts: - logger.warning(('\t' + red("Failed to split the following pkg: " - "%s, not a valid cat/pkg-ver" %cpv))) - continue - - cp = parts[0] + '/' + parts[1] - try: - slot = portdb.aux_get(cpv, ["SLOT"]) - except KeyError: - match, slot = get_best_match(cpv, cp, logger) - if not match: - logger.warning('\t' + red("Installed package: " - "%s is no longer available" %cp)) - continue - - if slot[0]: - cps.append(cp + ":" + slot[0]) - else: - cps.append(cp) - - return cps - - - -if __name__ == '__main__': - print('Nothing to call here') + """Uses portage to reduce the cpv list into a cp:slot list and returns it""" + from portage.versions import catpkgsplit + from portage import portdb + + cps = [] + for cpv in cpvs: + parts = catpkgsplit(cpv) + if not parts: + logger.warning( + ( + "\t" + + red( + "Failed to split the following pkg: " + "%s, not a valid cat/pkg-ver" % cpv + ) + ) + ) + continue + + cp = parts[0] + "/" + parts[1] + try: + slot = portdb.aux_get(cpv, ["SLOT"]) + except KeyError: + match, slot = get_best_match(cpv, cp, logger) + if not match: + logger.warning( + "\t" + red("Installed package: " "%s is no longer available" % cp) + ) + continue + + if slot[0]: + cps.append(cp + ":" + slot[0]) + else: + cps.append(cp) + + return cps + + +if __name__ == "__main__": + print("Nothing to call here") |