warning-parser.py
author Benjamin Smedberg <benjamin@smedbergs.us>
Fri, 05 Dec 2008 16:20:28 -0500
changeset 2 ef65f2093ed3
parent 1 95655dd58126
child 4 923e095ab9e7
permissions -rw-r--r--
Have the warning parser emit logging data, partly so that buildbot won't time out, and partly so that people can see who got blamed without referring to the database.
#!/usr/bin/env python

"""
Reads a build log on stdin. Parse warning messages (from GCC and elsewhere)
and store them in a sqlite database for later consumption.

Uses gmake "Entering directory" and "Leaving directory" messages to
keep track of the working directory. Resolves relative file paths against
these working directories, and also follows symlinks back into the
source tree.
"""

import sys, os, sqlite3, re, mercurial.hg, mercurial.ui, mercurial.revlog, mercurial.node

(srcdir, blamedb, dbname, logfile) = sys.argv[1:]

srcdir = os.path.realpath(srcdir) + '/'

blamedb = sqlite3.connect(blamedb)
blamecur = blamedb.cursor()

repo = mercurial.hg.repository(mercurial.ui.ui(), srcdir)
ctx = repo.changectx('.')

def getCVSBlame(file, line):
    blamecur.execute('''SELECT blame.who, blame.rev
                        FROM blame, files
                        WHERE
                          files.file = ? AND
                          files.id = blame.fileid AND
                          blame.minline <= ? AND blame.maxline >= ?''', (file, line, line))
    r = blamecur.fetchone()
    if r is None:
        print "Couldn't find CVS blame for %s:%i" % (file, line)
        return (None, None)

    who, rev = r
    return (who.replace('%', '@'), 'cvs:%s:%s' % (rev, file))

def getBlame(file, line):
    try:
        fctx = ctx[file]
    except mercurial.revlog.LookupError:
        return (None, None)

    (blamectx, linenumber), line = fctx.annotate(follow=True, linenumber=True)[line - 1]
    if blamectx.rev() == 1:
        # Walk backwards into CVS history
        return getCVSBlame(blamectx.path(), linenumber)

    return (blamectx.user(), "hg:%s:%s" % (mercurial.node.hex(blamectx.node()), blamectx.path()))

if os.path.exists(dbname):
    os.unlink(dbname)

db = sqlite3.connect(dbname)
dbcur = db.cursor()
dbcur.execute('''CREATE TABLE wdata
                 (
                    id INTEGER NOT NULL,
                    ord INTEGER NOT NULL,
                    file TEXT NOT NULL,
                    line INTEGER,
                    blame TEXT,
                    blamerev TEXT,
                    msg TEXT NOT NULL,
                    CONSTRAINT pkey PRIMARY KEY (id, ord)
                 )''')

cwdre = re.compile(r'g?make(\[\d+\])?: (Entering|Leaving) directory `(.*)\'$')
warningre = re.compile(r'(?P<file>[-/\.\w<>]+):((?P<line>\d+):)?(\d+:)? warning: (?P<msg>[^ ].*)$')
continuere = re.compile(r'(?P<file>[-/\.\w<>]+):((?P<line>\d+):)?(\d+:)?( warning:)?   (?P<msg>.*)$')

cwdstack = [os.getcwd()]

curid = -1
curord = -1

for line in open(logfile):
    line = line.strip()

    m = cwdre.match(line)
    if m is not None:
        e, wd = m.group(2, 3)
        if e == "Entering":
            cwdstack.append(wd)
        else:
            cwdstack.pop()
        continue

    m = warningre.match(line)
    if m is not None:
        curid += 1
        curord = 0
    else:
        m = continuere.match(line)
        if m is None:
            if line.find('warning') != -1:
                print "Found unexpected 'warning': %s" % line

            curord = -1
            continue

        if curord == -1:
            raise Exception("Found warning continuation before warning!: %s" % line)

    file, lineno, msg = m.group('file', 'line', 'msg')
    file = os.path.join(cwdstack[-1], file)
    file = os.path.realpath(file)

    if lineno is not None:
        lineno = int(lineno)

    blame = None

    if file.startswith(srcdir):
        file = file[len(srcdir):]
        blame, blamerev = getBlame(file, lineno)

    dbcur.execute('''INSERT INTO wdata (id, ord, file, line, blame, blamerev, msg) VALUES (?, ?, ?, ?, ?, ?, ?)''', (curid, curord, file, lineno, blame, blamerev, msg))

    if curord == 0:
        print "%s blamed on %s in revision %s" % (line, blame, blamerev)

    curord += 1

dbcur.close()
db.commit()
db.close()

print "TinderboxPrint:warn:%i" % (curid + 1)