author Benjamin Smedberg <benjamin@smedbergs.us>
Fri, 05 Dec 2008 16:20:28 -0500
changeset 2 ef65f2093ed31e6e484fbcaf29e4ab92aff25e03
parent 1 95655dd58126f8b872b65b78e1ea4ca089e8cc45
child 4 923e095ab9e75a60a8a4dd20958c191667ce1ddc
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
                          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):
        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):

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":

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

            curord = -1

        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


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