#!/usr/bin/python

# This script generates a report on the packaging status of X.org
# releases in Buildroot. It does so by downloading the list of
# tarballs that are part of a given X.org release, and compare that
# with the packages that are available in Buildroot.

import BeautifulSoup
import re
import os
import urllib
from distutils.version import LooseVersion

# This can be customized
XORG_VERSION = "X11R7.7"

# Key names in dictionaries
XORG_VERSION_KEY = "xorg-version"
BR_VERSION_KEY = "br-version"
BR_NAME_KEY = "br-name"

# Packages part of X.org releases that we do not want to package in
# Buildroot (old drivers for hardware unlikely to be used in embedded
# contexts).
XORG_EXCEPTIONS = [
    'xf86-video-suncg6',
    'xf86-video-sunffb',
]

# Get the list of tarballs of a X.org release, parse it, and return a
# dictionary of dictionaries, of the form:
#
#   { <name_of_package> : { XORG_VERSION_KEY: <version_of_package> },
#     <name_of_package2> : { XORG_VERSION_KEY: <version_of_package2> }}
#
def get_xorg_release_pkgs():
    u = urllib.URLopener().open("http://www.x.org/releases/%s/src/everything/" % XORG_VERSION)
    b = BeautifulSoup.BeautifulSoup()
    b.feed(u.read())
    links = b.findAll("a")
    packages = {}
    r = re.compile("(.*)-([0-9\.]*).tar.bz2")
    # We now have a list of all links.
    for link in links:
        href = link.get("href")
        # Skip everything but tarballs
        if not href.endswith(".tar.bz2"):
            continue
        # Separate the name and the version
        groups = r.match(href)
        if not groups:
            continue
        name = groups.group(1)
        version = groups.group(2)
        # Skip packages we don't want to hear about
        if name in XORG_EXCEPTIONS:
            continue
        packages[name] = { XORG_VERSION_KEY : version }
    return packages

# Files and directories in package/x11r7/ that should be ignored in
# our processing.
BUILDROOT_EXCEPTIONS = [
    "mcookie", # Code is directly in package directory
    "x11r7.mk",
    "Config.in",
    "xdriver_xf86-input-tslib", # From Pengutronix, not part of X.org releases
]

# Prefixes of directories in package/x11r7/ that must be stripped
# before trying to match Buildroot package names with X.org tarball
# names.
BUILDROOT_PREFIXES = [
    "xapp",
    "xdriver",
    "xfont",
    "xlib",
    "xserver",
    "xutil",
    "xproto",
]

# From a Buildroot package name, try to see if a prefix should be
# stripped from it. For example, passing "xapp_xlsfonts" as argument
# to this function will return "xlsfonts".
def buildroot_strip_prefix(dirname):
    for prefix in BUILDROOT_PREFIXES:
        if dirname.startswith(prefix + "_"):
            return dirname[len(prefix) + 1:]
    return dirname

# From a Buildroot package name, parse its .mk file to find the
# Buildroot version of the package by looking at the <foo>_VERSION
# line.
def buildroot_get_version(dirname):
    f = open(os.path.join("package", "x11r7", dirname, dirname + ".mk"))
    r = re.compile("^([A-Z0-9_]*)_VERSION = ([0-9\.]*)$")
    for l in f.readlines():
        m = r.match(l)
        if m:
            return m.group(2)
    return None

# Augment the information of the X.org list of packages (given as
# argument) by details about their packaging in Buildroot. Those
# details are found by looking at the contents of package/x11r7/.
def get_buildroot_pkgs(packages):
    dirs = os.listdir(os.path.join(os.getcwd(), "package", "x11r7"))
    for d in dirs:
        # Skip exceptions
        if d in BUILDROOT_EXCEPTIONS:
            continue
        pkgname = buildroot_strip_prefix(d)
        version = buildroot_get_version(d)
        if packages.has_key(pkgname):
            # There is a X.org package of the same name, so we just
            # add information to the existing dict entry.
            packages[pkgname]['br-version'] = version
            packages[pkgname]['br-name'] = d
        else:
            # There is no X.org package with this name, so we add a
            # new dict entry.
            packages[pkgname] = { BR_VERSION_KEY: version,
                                  BR_NAME_KEY : d }
    return packages

def show_summary(packages):
    FORMAT_STRING = "%40s | %15s | %15s | %-30s"
    print FORMAT_STRING % ("Package name", "Vers in BR", "Vers in X.org", "Action")
    print FORMAT_STRING % ("-" * 40, "-" * 15, "-" * 15, "-" * 30)
    pkgs = packages.keys()
    pkgs.sort()
    total_pkgs = 0
    upgrade_pkgs = 0
    add_pkgs = 0
    remove_pkgs = 0
    nothing_todo_pkgs = 0
    for pkgname in pkgs:
        pkg = packages[pkgname]
        total_pkgs += 1
        if pkg.has_key(XORG_VERSION_KEY) and not pkg.has_key(BR_VERSION_KEY):
            xorg_version = pkg[XORG_VERSION_KEY]
            br_version = "N/A"
            action = "Add to Buildroot"
            add_pkgs += 1
        elif not pkg.has_key(XORG_VERSION_KEY) and pkg.has_key(BR_VERSION_KEY):
            br_version = pkg[BR_VERSION_KEY]
            xorg_version = "N/A"
            action = "Remove from Buildroot"
            remove_pkgs += 1
        elif LooseVersion(pkg[XORG_VERSION_KEY]) > LooseVersion(pkg[BR_VERSION_KEY]):
            br_version = pkg[BR_VERSION_KEY]
            xorg_version = pkg[XORG_VERSION_KEY]
            action = "Upgrade"
            upgrade_pkgs += 1
        elif LooseVersion(pkg[XORG_VERSION_KEY]) < LooseVersion(pkg[BR_VERSION_KEY]):
            br_version = pkg[BR_VERSION_KEY]
            xorg_version = pkg[XORG_VERSION_KEY]
            action = "More recent"
            nothing_todo_pkgs += 1
        else:
            br_version = pkg[BR_VERSION_KEY]
            xorg_version = pkg[XORG_VERSION_KEY]
            action = ""
            nothing_todo_pkgs += 1

        print FORMAT_STRING % (pkgname, br_version.center(15), xorg_version.center(15), action)
    print FORMAT_STRING % ("-" * 40, "-" * 15, "-" * 15, "-" * 30)
    STAT_FORMAT_STRING = "%40s : %3d"
    print STAT_FORMAT_STRING % ("Total number of packages", total_pkgs)
    print STAT_FORMAT_STRING % ("Packages to upgrade", upgrade_pkgs)
    print STAT_FORMAT_STRING % ("Packages to add", add_pkgs)
    print STAT_FORMAT_STRING % ("Packages to remove", remove_pkgs)
    print STAT_FORMAT_STRING % ("Packages with nothing to do", nothing_todo_pkgs)

packages = get_xorg_release_pkgs()
packages = get_buildroot_pkgs(packages)
# print packages
show_summary(packages)