#!/usr/bin/env python

products = {
    'workstation' : {
        'srcpkg':       'vmware',
        'filename':     'VMware-workstation-([0-9.-]+)\.(i386|x86_64)\.tar\.gz',
        'version':      ('6.0.3.80004',),
        'md5sum':       ('57601f238106cb12c1dea303ad1b4820', 'd8d423a76f99a94f598077d41685e9a9'),
    },

    'player': {
        'srcpkg':       'vmware',
        'filename':     'VMware-player-([0-9.-]+)\.(i386|x86_64)\.tar\.gz',
        'version':      ('2.0.3.80004',),
        'md5sum':       ('2305fcff49bef6e4ad83742412eac978', 'f99c5b293eb87c5f918ad24111565b9f'),
    },

    'server' : {
        'srcpkg':       'vmware',
        'filename':     'VMware-server-([0-9.-]+)\.tar\.gz',
        'version':      ('1.0.5.80187',),
        'md5sum':       '161dcbe5af9bbd9834a86bf7c599903e',
    },

    'server-console' : {
        'srcpkg':       'vmware',
        'filename':     'VMware-server-console-([0-9.-]+)\.tar\.gz',
        'version':      ('1.0.5.80187',),
        'md5sum':       '1dc2230f07c3468008e8a528836b6af9',
    },

    'server-mui': {
        'srcpkg':       'vmware',
        'filename':     'VMware-mui-([0-9.-]+)\.tar\.gz',
        'version':      ('1.0.5.80187',),
        'md5sum':       'dd10d25895d9994bd27ca896152f48ef',
    },

    'any-any' : {
        'srcpkg':       'any-any',
        'filename':     'vmware-any-any-update(.*)\.tar\.gz',
        'version':      ('115',),
        'md5sum':       'ab33ff7a799fee77f0f4ba5667cd4b9a',
    },
}

products_kernel = ('any-any', 'player', 'server', 'workstation')

from optparse import OptionParser
from subprocess import call, Popen, PIPE
import os
import pwd
import re
import rfc822
import socket
import sys

VERSION = '0.22'
SHARE = '/usr/share/vmware-package'
DOC = '/usr/share/doc/vmware-package'

def log(s):
    print >>sys.stderr, ' ===>', s

def sh(cmd):
    print cmd.strip()
    rc = call(cmd, shell=True)
    if rc != 0:
        print >>sys.stderr, ' ===> error, exiting with return value', rc
        sys.exit(rc)

def maintainer():
    name = 'Unknown local maintainer'
    email = 'unknown@invalid.tld'
    try:
        name = os.getenv('DEBFULLNAME')
        if not name:
            name = pwd.getpwuid(os.getuid()).pw_gecos.split(',')[0]
    except:
        pass

    try:
        email = os.getenv('DEBEMAIL')
        if not email:
            email = os.getenv('EMAIL')
        if not email:
            email = pwd.getpwuid(os.getuid()).pw_name + '@' + socket.getfqdn()
    except:
        pass

    maint = '%s <%s>' % (name, email)
    m = rfc822.parseaddr(maint)
    if m[0] == '' or m[1] == '':
        invalid_maint(maint)
    return maint

def product(opt, fname):
    if opt.product in products:
        log('forcing product "%s"' % opt.product)
        return
    elif opt.product and not opt.product in products:
        unknown_product()

    for pr in products:
        expr = products[pr]['filename']
        match = re.match(expr, os.path.basename(fname))
        if match:
            opt.product = pr
            log('detected product "%s"' % opt.product)
            return
    unknown_product()

def upstreamver(opt, fname):
    if opt.upstreamver:
        log('forcing upstream version "%s"' % opt.upstreamver)
        return
    for pr in products:
        expr = products[pr]['filename']
        match = re.match(expr, os.path.basename(fname))
        if match:
            opt.upstreamver = match.groups()[0].replace('-', '.')
            if not opt.skipchecks and not opt.upstreamver in products[pr]['version']:
                    unknown_version()
            else:
                log('detected upstream version "%s"' % opt.upstreamver)
                return
    unknown_version()

def checkmd5(opt, fname):
    if not opt.skipchecks:
        md5 = Popen(['md5sum', fname], stdout=PIPE).communicate()[0].split()[0]
        if not md5 in products[opt.product]['md5sum']:
            unknown_md5sum()
    log('md5sum check passed')

def build(opt, fname):
    log('debianizing %s' % fname)
    product(opt, fname)
    upstreamver(opt, fname)
    if not opt.skipchecks:
        checkmd5(opt, fname)

    pkgver = '.'.join((opt.upstreamver, VERSION, opt.localver))
    pkgstring = 'vmware-%s-%s' % (opt.product, pkgver)
    pkgdir = '%s/vmware-%s' % (opt.builddir, opt.product)
    builddir = '%s/%s' % (pkgdir, pkgstring)
    srcpkg = products[opt.product]['srcpkg']
    srcpkgdir = SHARE + '/' + srcpkg

    log('vmware-package %s build starting in %s' % (VERSION, builddir))

    log('creating source package from ' + srcpkgdir)
    if os.path.isdir(builddir):
        builddir_exists(builddir)
    sh('mkdir -p %s' % builddir)
    sh('cp -a %s/* %s' % (srcpkgdir, builddir))
    sh('cp %s/copyright %s/debian' % (DOC, builddir))
    sh('cp %s/changelog %s/debian' % (SHARE, builddir))
    sh('cp %s/vmware.mk %s/debian' % (SHARE, builddir))
    
    sed = 'sed -i \
        -e "s%%@PRODUCT@%%%(product)s%%g" \
        -e "s%%@TARBALL@%%%(fname)s%%g" \
        -e "s%%@PACKAGE@%%vmware-%(product)s%%g" \
        -e "s%%@UPSTREAM_VERSION@%%%(upstreamver)s%%g" \
        -e "s%%@PACKAGE_VERSION@%%%(pkgver)s%%g" \
        -e "s%%@DEB_VERSION@%%%(debver)s%%g" \
        -e "s%%@DISTRIBUTION@%%%(distribution)s%%g" \
        -e "s%%@MAINTAINER@%%%(maintainer)s%%g" \
        -e "s%%@TIMESTAMP@%%`date -R`%%g" \
        `find %(builddir)s -type f`' % {
            'product': opt.product,
            'fname': fname,
            'pkgver': pkgver,
            'debver': VERSION,
            'distribution': opt.distribution,
            'upstreamver': opt.upstreamver,
            'maintainer': opt.maintainer,
            'builddir': builddir,
    }

    log('populating template values')
    sh(sed)

    if products[opt.product]['srcpkg'] == 'vmware':
        log('installing control files')
        sh('cd %s/debian && ln -sf control.%s control' % (builddir, opt.product))

    if opt.product == 'player':
        sh('cd %s/debian && ln -sf config.workstation config.player' % builddir)

    log('symlinking distributed tarball')
    sh('ln -sf %s %s' % (fname, builddir))

    if not opt.dryrun:
        log('starting package build')
        sh('cd %s && dpkg-buildpackage -B -b -uc -r%s' % (builddir, opt.rootcmd))
    else:
        log('package build directory left in %s' % builddir)
        return

    if not opt.nodelete:
        log('deleting source package directory')
        sh('%s rm -rf %s' % (opt.rootcmd, builddir))

    if opt.kernel and opt.product in products_kernel and \
            (opt.rootcmd != 'fakeroot' or os.geteuid() == 0):

        if opt.rootcmd == 'fakeroot':
            rootcmd = ''
        else:
            rootcmd = opt.rootcmd

        if opt.product == 'any-any':
            pkg = 'vmware-any-any-kernel-source'
        if opt.product == 'server':
            pkg = 'vmware-server-kernel-source'
        if opt.product in ('workstation', 'player'):
            pkg = 'vmware-kernel-source'
        deb = '%s_%s_all.deb' % (pkg, pkgver)

        log('installing kernel source package')
        sh('%s dpkg --install %s/%s' % (rootcmd, pkgdir, deb))

        log('building kernel modules')
        ma_opts = [ '--force', '--text-mode', '--userdir %s' % pkgdir ]
        if opt.kverslist:
            ma_opts.append('--kvers-list %s' % opt.kverslist)
        sh('%s m-a %s build %s' % (rootcmd, ' '.join(ma_opts), pkg))
        sh('%s rm -rf %s/usr_src %s/var_cache_modass' % (rootcmd, pkgdir, pkgdir))

        log('deinstalling kernel source package')
        sh('%s dpkg --remove %s' % (opt.rootcmd, pkg))

    opt.product = None
    opt.upstreamver = None

def unknown_md5sum():
    print >>sys.stderr, 'unknown or unsupported md5sum, try using -s'
    sys.exit(2)

def unknown_product():
    print >>sys.stderr, 'unable to determine product type, try using -p'
    sys.exit(3)

def unknown_version():
    print >>sys.stderr, 'unsupported product version, try using -s or -u'
    sys.exit(4)

def builddir_exists(bd):
    print >>sys.stderr, 'build directory already exists:', bd
    sys.exit(5)

def invalid_maint(maint):
    print >>sys.stderr, 'invalid maintainer field "%s"' % maint
    sys.exit(6)

def multi_file_err():
    print >>sys.stderr, 'cannot specify -p or -u and multiple files'
    sys.exit(7)

def main():
    parser = OptionParser('usage: %prog [options] <file>',
        version='%prog ' + VERSION)

    parser.add_option('-b', '--builddir', dest='builddir',
        default=os.path.realpath('.'),
        help='package build directory')
    
    parser.add_option('-d', '--nodelete', dest='nodelete',
        action='store_true',
        default=False,
        help='refrain from deleting the source package directory')

    parser.add_option('-D', '--distribution', dest='distribution',
        default='UNRELEASED',
        help='set distribution name')

    parser.add_option('-k', '--kernel', dest='kernel',
        action='store_true',
        default=False,
        help='build kernel modules (requires root privileges)')

    parser.add_option('-K', '--kvers-list', dest='kverslist',
        default=None,
        help='specific kernel versions to build modules for (passed to m-a)')

    parser.add_option('-l', '--localver', dest='localver',
        default='0',
        help='local version (appended to the upstream version)')
    
    parser.add_option('-m', '--maintainer', dest='maintainer',
        default=maintainer(),
        help='local maintainer name and email address')
    
    parser.add_option('-n', '--dryrun', dest='dryrun',
        action='store_true',
        default=False,
        help='perform a dry run, do not begin the package build (implies -d)')

    parser.add_option('-r', '--rootcmd', dest='rootcmd',
        default='fakeroot',
        help='command to gain root privileges during package build')

    parser.add_option('-s', '--skipchecks', dest='skipchecks', action='store_true',
        help='skip version and md5sum checks')

    parser.add_option('-p', '--product', dest='product',
        help='attempt to build a specific product')

    parser.add_option('-u', '--upstreamver', dest='upstreamver',
        help='force the upstream version')

    opt, fname = parser.parse_args()
    if not fname:
        parser.print_help()
        sys.exit(1)

    if (opt.product or opt.upstreamver) and len(fname) > 1:
        multi_file_err()

    for f in fname:
        build(opt, os.path.realpath(f))

if __name__ == '__main__':
    main()
