# Copyright (C) 2012 Sebastian Pipping # Licender under GPL v2 or later from __future__ import print_function VERSION_STR = '0.4.1' import sys import os import portage import re try: import argparse except ImportError: print("ERROR: You need Python 2.7+ unless you have module argparse " "(package dev-python/argparse on Gentoo) installed independently.", file=sys.stderr) sys.exit(1) _revision_matcher = re.compile('-r([0-9]+)$') def find_atoms(repository_path, category_package): versions = list() category, package = category_package.split('/') for root, dirs, files in os.walk(os.path.join(repository_path, category_package)): for f in files: if not f.endswith('.ebuild'): continue versions.append(f[len(package) + 1:-len('.ebuild')]) return versions def highest_revision_only(versions): def make_version(version, revison): if revision == 0: return version return '%s-r%s' % (version, revison) d = dict() for v in versions: match = _revision_matcher.search(v) if match is None: revision = 0 version = v else: revision = int(match.group(1)) version = v[:-len(match.group(0))] d[version] = max(d.get(version, 0), revision) return [make_version(version, revison) for version, revison in d.items()] def find_missed_bumps(gentoo_versions, overlay_versions): missed_version_bumps = list() missed_revision_bumps = list() gentoo_zero_revisions = set(_revision_matcher.sub('', gv) for gv in gentoo_versions) for ov in overlay_versions: if '999' in ov: continue newer_than_gentoo = True for gv in gentoo_versions: if '999' in gv: continue if portage.vercmp(ov, gv) <= 0: newer_than_gentoo = False break if newer_than_gentoo: ov_without_revision = _revision_matcher.sub('', ov) if ov_without_revision in gentoo_zero_revisions: missed_revision_bumps.append(ov) else: missed_version_bumps.append(ov) return (missed_revision_bumps, missed_version_bumps) def find_ebuild_changes(category_package, overlay_path, gentoo_versions, overlay_versions): ebuild_changes = list() intersection = set(gentoo_versions) & set(overlay_versions) if not intersection: return list() category, package = category_package.split('/') for version in intersection: ebuild_name = '%s-%s.ebuild' % (package, version) command = """bash -c 'FILTER() { sed -e "/# $Header/d" -e "/# Copyright/d" -e "/KEYWORDS=/d" "$@"; }; """ \ """cmp --quiet <(FILTER /usr/portage/%s/%s) <(FILTER %s/%s/%s) ; """ \ """exit $?'""" \ % (category_package, ebuild_name, overlay_path, category_package, ebuild_name) ret = os.system(command) if not ret: continue # print("meld '/usr/portage/%s/%s' '%s/%s/%s'" % (category_package, ebuild_name, conf.overlay_path, category_package, ebuild_name)) ebuild_changes.append(version) return ebuild_changes def sorted_versions(versions): return sorted(versions, cmp=portage.vercmp) def dump_tree(tree, title): print('===============================================================') print(title) print('===============================================================') for category, package_versions in sorted(tree.items()): print('%s/' % category) for package, versions in sorted(package_versions.items()): print(' %s :: %s' % (package, ', '.join(sorted_versions(versions)))) print() def parse_command_line(args): parser = argparse.ArgumentParser(description='Simple tool for static analysis of Gentoo overlays') parser.add_argument( '--version', action='version', version='%(prog)s ' + VERSION_STR) parser.add_argument('overlay_path', metavar='PATH', action='store', help='Path to Gentoo overlay') conf = parser.parse_args(args=args[1:]) return conf def main(args): conf = parse_command_line(args) def sanitize_overlay_path(path): path = path.rstrip('/') if path == '': return '/' return path conf.overlay_path = sanitize_overlay_path(conf.overlay_path) missed_revision_bumps_tree = dict() missed_version_bumps_tree = dict() ebuild_changes_tree = dict() for root, dirs, files in os.walk(conf.overlay_path): if '.svn' in dirs: dirs[:] = [d for d in dirs if d != '.svn'] for d in dirs: full_path_overlay = os.path.join(root, d) category_package = full_path_overlay[len(conf.overlay_path + '/'):] if len(category_package.split('/')) != 2: continue full_path_gentoo = os.path.join('/usr/portage/', category_package) found = os.path.exists(full_path_gentoo) if not found: continue overlay_versions = find_atoms(conf.overlay_path, category_package) gentoo_versions = find_atoms('/usr/portage/', category_package) (missed_revision_bumps, missed_version_bumps) = find_missed_bumps(gentoo_versions, overlay_versions) ebuild_changes = find_ebuild_changes(category_package, conf.overlay_path, gentoo_versions, overlay_versions) category, package = category_package.split('/') if missed_revision_bumps: missed_revision_bumps_tree.setdefault(category, dict())[package] = highest_revision_only(missed_revision_bumps) if missed_version_bumps: missed_version_bumps_tree.setdefault(category, dict())[package] = highest_revision_only(missed_version_bumps) if ebuild_changes: ebuild_changes_tree.setdefault(category, dict())[package] = ebuild_changes dump_tree(missed_version_bumps_tree, 'Version bumps missing from Gentoo main tree') dump_tree(missed_revision_bumps_tree, 'Revision bumps missing from Gentoo main tree') dump_tree(ebuild_changes_tree, 'Ebuils that differ at same revision') return 0 def go(): ret = main(sys.argv) sys.exit(ret)