generate-automatic-patches.py
author Benjamin Smedberg <benjamin@smedbergs.us>
Sat, 26 Jul 2008 22:49:39 -0400
changeset 167 a4da40849f5436e629c5732f4368c6c48189637f
parent 165 aef82785a2308e43efecc8fa9addeb0456d09895
permissions -rw-r--r--
State as of now

#!/usr/bin/env python

"""Generate automatic patches and add them to your tree.

You will need to set up a separate xpcomgclocalpaths.py file with the following contents:

oink = '/path/to/oink'
porkbarrel = '/path/to/pork-barrel'
configureporkanalysis = '/path/to/configure-pork-analysis'
configuregccanalysis = '/path/to/configure-gcc-analysis'
parallel = 8 # number of parallel processes to use for make -j and pork-barrel

For an example of configure-pork-analysis and configure-gcc-analysis, see http://hg.mozilla.org/users/bsmedberg_mozilla.com/configure-scripts
"""

from __future__ import with_statement

import sys, getopt
from os.path import exists, realpath, normpath, dirname
from os import mkdir, symlink, unlink
from subprocess import call, check_call, Popen
from shutil import rmtree

from xpcomgclocalpaths import oink, porkbarrel, configureporkanalysis, configuregccanalysis, parallel

thisfile = realpath(__file__)
thisdir = dirname(thisfile)
rootdir = normpath('%s/../../..' % thisdir)
srcdir = '%s/src' % rootdir
porkobjdir = '%s/ff-pork' % rootdir
nukerobjdir = '%s/ff-nuker' % rootdir
analysisobjdir = '%s/ff-gcobject-analysis' % rootdir
clobber = True
garburator = '%s/garburator' % oink
nuker = '%s/nuker' % oink

def setup_srcdir(log):
    """Remove old automatic patches, if any"""

    check_call(['hg', 'qpop', '-a'], cwd=srcdir, stdout=log, stderr=log)
    call(['hg', 'qrm', 'automatic-garburator'], cwd=srcdir, stdout=log, stderr=log)
    call(['hg', 'qrm', 'automatic-nuker'], cwd=srcdir, stdout=log, stderr=log)
    call(['hg', 'qrm', 'automatic-gcobject'], cwd=srcdir, stdout=log, stderr=log)
    call(['hg', 'qrm', 'automatic-remove-addrefs'], cwd=srcdir, stdout=log, stderr=log)
    check_call(['hg', 'qpush', '-a'], cwd=srcdir, stdout=log, stderr=log)
    check_call(['autoconf-2.13'], cwd=srcdir, stdout=log, stderr=log)

def build_pork(log):
    """Build pork objdir"""

    if exists(porkobjdir) and clobber:
            rmtree(porkobjdir)
    if not exists(porkobjdir):
        mkdir(porkobjdir)

    with open('%s/pork-build.log' % rootdir, 'w') as errlog:
        check_call([configureporkanalysis], cwd=porkobjdir,
                   stdout=errlog, stderr=errlog)

        if exists('%s/config/myrules.mk' % porkobjdir):
            unlink('%s/config/myrules.mk' % porkobjdir)
        symlink('%s/pork-rules.mk' % thisdir, '%s/config/myrules.mk' % porkobjdir)

        check_call(['make', '-j%i' % parallel, 'WARNINGS_AS_ERRORS='],
                   cwd=porkobjdir,
                   stdout=errlog, stderr=errlog)

        if exists('%s/iifiles.list' % rootdir):
            unlink('%s/iifiles.list' % rootdir)
        check_call(['make', 'echo-iifiles', 'IIFILES_LIST=%s/iifiles.list' % rootdir],
                   cwd=porkobjdir, stdout=errlog, stderr=errlog)

def run_garburator(log):
    """Run garburator"""

    with open('%s/automatic-garburator.errlog' % rootdir, 'w') as garburator_log:
        with open('%s/automatic-garburator.patch' % rootdir, 'w+') as garburator_patch:
            check_call([porkbarrel, '%i' % parallel, '%s/iifiles.list' % rootdir, garburator],
                       cwd=porkobjdir,
                       stdout=garburator_patch,
                       stderr=garburator_log)

            garburator_patch.seek(0)

            with open('%s/automatic-garburator-filtered.patch' % rootdir, 'w') as garburator_filtered_patch:
                check_call(['filterdiff', '-x', '**nsCOMPtr.h'],
                           stdin=garburator_patch,
                           stdout=garburator_filtered_patch,
                           stderr=garburator_log)

def apply_garburator(log):
    """Apply the garburator patch"""

    check_call(['hg', 'qnew', 'automatic-garburator'],
               cwd=srcdir, stdout=log, stderr=log)
    with open('%s/automatic-garburator-filtered.patch' % rootdir) as garburator_filtered_patch:
        check_call(['patch', '-p0'], cwd=porkobjdir,
                   stdin=garburator_filtered_patch,
                   stdout=log, stderr=log)
    check_call(['hg', 'qrefresh', '-m', 'Automatically generated patch: convert stack nsCOMPtrs to raw pointers'],
               cwd=srcdir, stdout=log, stderr=log)

def build_nuker(log):
    """Build nuker objdir"""

    if exists(nukerobjdir) and clobber:
            rmtree(nukerobjdir)
    if not exists(nukerobjdir):
        mkdir(nukerobjdir)

    with open('%s/nuker-build.log' % rootdir, 'w') as errlog:
        check_call([configureporkanalysis], cwd=nukerobjdir,
                   stdout=errlog, stderr=errlog)

        check_call(['make', '-j%i' % parallel, 'WARNINGS_AS_ERRORS='],
                   cwd=nukerobjdir,
                   stdout=errlog, stderr=errlog)

def run_nuker(log):
    """Run nuker"""

    with open('%s/automatic-nuker.errlog' % rootdir, 'w') as nuker_log:
        with open('%s/automatic-nuker.patch' % rootdir, 'w') as nuker_patch:
            check_call([porkbarrel, '%i' % parallel, '%s/iifiles.list' % rootdir, nuker, '-class-list', '%s/GCObject.classlist' % thisdir],
                       cwd=nukerobjdir,
                       stdout=nuker_patch,
                       stderr=nuker_log)

def apply_nuker(log):
    """Apply the nuker patch"""

    check_call(['hg', 'qnew', 'automatic-nuker'],
               cwd=srcdir, stdout=log, stderr=log)
    with open('%s/automatic-nuker.patch' % rootdir) as nuker_patch:
        check_call(['patch', '-p0'], cwd=nukerobjdir,
                   stdin=nuker_patch,
                   stdout=log, stderr=log)
    check_call(['hg', 'qrefresh', '-m', 'Automatically generated patch: make a static list of classes inherit from GCObject, and remove destructors and such'],
               cwd=srcdir, stdout=log, stderr=log)

def build_gcobject(log):
    """Build the GCObject analysis"""

    if exists(analysisobjdir) and clobber:
            rmtree(analysisobjdir)
    if not exists(analysisobjdir):
        mkdir(analysisobjdir)

    with open('%s/analysis-build.log' % rootdir, 'w') as errlog:
        check_call([configuregccanalysis], cwd=analysisobjdir,
                   stdout=errlog, stderr=errlog)

        if exists('%s/config/myconfig.mk' % analysisobjdir):
            unlink('%s/config/myconfig.mk' % analysisobjdir)
        symlink ('%s/gcobject-config.mk' % thisdir,
                 '%s/config/myconfig.mk' % analysisobjdir)

        check_call(['make', '-j%i' % parallel, 'WARNINGS_AS_ERRORS='],
                   cwd=analysisobjdir,
                   stdout=errlog, stderr=errlog)

def run_gcobject(log):
    """Generate the GCObject patch"""

    # Turn this into a module instead of a subprocess
    with open('%s/automatic-gcobject.errlog' % rootdir, 'w') as errlog:
        with open('%s/automatic-gcobject.patch' % rootdir, 'w') as patch:
            check_call(['python', '%s/add-gcobject-inheritance.py' % thisdir,
                        analysisobjdir],
                       stdout=patch,
                       stderr=errlog)

def apply_gcobject(log):
    """Apply the GCObject patch"""

    check_call(['hg', 'qnew', 'automatic-gcobject'],
               cwd=srcdir, stdout=log, stderr=log)
    with open('%s/automatic-gcobject.patch' % rootdir) as gcobject_patch:
        check_call(['patch', '-p0'],
                   cwd=srcdir,
                   stdin=gcobject_patch,
                   stdout=log, stderr=log)
    check_call(['hg', 'qrefresh', '-m', 'Automatically generated patch: make all XPCOM objects inherit from XPCOMGCFinalizedObject'],
               cwd=srcdir, stdout=log, stderr=log)

def run_removeaddrefs(log):
    """Remove NS_ADDREF and similar macro-junk"""

    # Turn this into a module instead of a subprocess
    with open('%s/automatic-remove-addrefs.patch' % rootdir, 'w') as patch:
        check_call(['python', '%s/remove-addrefs.py' % thisdir, srcdir],
                   stdout=patch,
                   stderr=log)

def apply_removeaddrefs(log):
    """Apply the addref removal patch"""
    check_call(['hg', 'qnew', 'automatic-remove-addrefs'],
               cwd=srcdir, stdout=log, stderr=log)
    with open('%s/automatic-remove-addrefs.patch' % rootdir) as addrefs_patch:
        check_call(['patch', '-p0'],
                   cwd=srcdir,
                   stdin=addrefs_patch,
                   stdout=log, stderr=log)
    check_call(['hg', 'qrefresh', '-m', 'Automatically generated patch: remove no-op calls to NS_ADDREF and NS_IF_ADDREF'],
               cwd=srcdir, stdout=log, stderr=log)

steps = (('setup_srcdir', setup_srcdir),
         ('build_pork', build_pork),
         ('run_garburator', run_garburator),
         ('apply_garburator', apply_garburator),
         ('build_nuker', build_nuker),
         ('run_nuker', run_nuker),
         ('apply_nuker', apply_nuker),
         ('build_gcobject', build_gcobject),
         ('run_gcobject', run_gcobject),
         ('apply_gcobject', apply_gcobject),
         ('run_removeaddrefs', run_removeaddrefs),
         ('apply_removeaddrefs', apply_removeaddrefs),
         )

# I would use optionparser, but I can't get it to format this
# usage the way I want

steplist = "".join("\n    %-20s %.50s" % (key, f.__doc__)
                   for key, f in steps)

usage = """python generated-patches.py [options]
  -h, --help       show this help message and exit
  --start=stepname Start at a particular step:%s
  --no-clobber     Rebuild objdirs without clobbering""" % steplist

options, arguments = getopt.getopt(sys.argv[1:], 'h',
                                   ('start=', 'no-clobber', 'help'))

startstep = 0
for name, value in options:
    if name in ('-h', '--help'):
        print usage
        sys.exit(0)

    if name == '--start':
        found = False
        for i in xrange(0, len(steps)):
            if steps[i][0] == value:
                startstep = i
                found = True
                break
        if not found:
            print >>sys.stderr, "'%s' is not a step" % value
            sys.exit(1)

    if name == '--no-clobber':
        clobber = False

with open('%s/generate-patches.log' % rootdir, 'w') as general_log:
    for i in xrange(startstep, len(steps)):
        key, f = steps[i]
        print "%s: %s..." % (key, f.__doc__),
        sys.stdout.flush()
        f(general_log)
        print "OK"
        sys.stdout.flush()