ANN: firehose-0.3 released

Alec Leamas leamas.alec at gmail.com
Thu Oct 10 12:50:10 UTC 2013


On 2013-10-08 18:46, David Malcolm wrote:
> "firehose" is a Python package intended for managing the results from
> code analysis tools (e.g. compiler warnings, static analysis, linters,
> etc).
>
> It currently provides parsers for the output of gcc, clang-analyzer,
> cppcheck, and findbugs.  These parsers convert the results into a common
> data model of Python objects, with methods for lossless roundtrips
> through a provided XML format.  There is also a JSON equivalent.
Don't really know what to do with this, but I enclose a simple rpmlint 
to firehose converter.  Perhaps it has a better home in "your" project.

It's a simple script, but it has been tested on the complete Fedora 
distribution so it should at least not crash that often. If you like it, 
just add it to the firehose repo.

--alec
-------------- next part --------------
#!/usr/bin/env python
#    This program is free software; you can redistribute it and/or modify
#    it under the terms of the GNU General Public License as published by
#    the Free Software Foundation; either version 2 of the License, or
#    (at your option) any later version.
#
#    This program is distributed in the hope that it will be useful,
#    but WITHOUT ANY WARRANTY; without even the implied warranty of
#    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
#    GNU General Public License for more details.
#
#    You should have received a copy of the GNU General Public License along
#    with this program; if not, write to the Free Software Foundation, Inc.,
#    51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
#
'''
Convert rpmlint output to firehose-compatible XML.

Synopsis:
    rpmlint2xml <specfile> <resultsfile>

specfile:
   The file being checked by rpmlint.
resultfile:
   The rpmlint results file.

Writes resulting xml on stdout.

See: https://github.com/fedora-static-analysis/firehose
'''

# pylint: disable=W0621,C0103


import re
import subprocess
import sys
import xml.etree.ElementTree as ET
import xml.dom.minidom

import rpm


def get_specfile(path):
    ' Return a (specfile, sha224sum) tuple. '
    path = path.strip()
    cs = subprocess.check_output(['sha224sum', path]).split()[0]
    if '/' in path:
        path = path.rsplit('/', 1)[1]
    return path, cs


def get_version():
    ''' rpmlint version. '''
    raw = subprocess.check_output(['rpmlint', '--version'])
    return re.search('[0-9]+[.][0-9]+', raw).group()


def parse_spec_nvr(specpath):
    ''' return name-version-release dict for spec. '''
    try:
        spec = rpm.TransactionSet().parseSpec(specpath)
    except Exception as ex:                     # pylint: disable=W0703
        print "Can't parse specfile: " + ex.__str__()
        sys.exit(2)
    header = spec.sourceHeader
    return {'name': header[rpm.RPMTAG_NAME],
            'version': header[rpm.RPMTAG_VERSION],
            'release': header[rpm.RPMTAG_RELEASE]}


def create_xmltree(path, nvr, cs, lintversion):
    ''' Create the basic xml report complete with <metadata>. '''
    root = ET.Element('analysis')
    metadata = ET.SubElement(root, 'metadata')
    ET.SubElement(metadata, 'generator', {'name': 'rpmlint',
                                          'version': lintversion})
    file_ = ET.SubElement(metadata, 'file', {'given-path': path})
    ET.SubElement(file_, 'hash', {'alg': 'sha224', 'hexdigest': cs})
    sut = ET.SubElement(metadata, 'sut')
    ET.SubElement(sut, 'source-rpm', nvr)
    ET.SubElement(root, 'results')
    return root


def add_xml_result(root, result, descriptions):
    ''' Add a rpmlint warning/error to the results. '''
    path = root.find('metadata/file').attrib['given-path']
    results = root.find('results')
    if not result['name'] in descriptions:
        d = subprocess.check_output(['rpmlint', '-I', result['name']])
        descriptions[result['name']] = d
    issue = ET.SubElement(results,
                          'issue',
                          {'test-id': result['name'],
                           'severity': result['severity']})
    message = ET.SubElement(issue, 'message')
    message.text = descriptions[result['name']]
    location = ET.SubElement(issue, 'location')
    ET.SubElement(location, 'file', {'given-path': path})
    if 'line' in result:
        ET.SubElement(location, 'point', {'line': result['line']})
    ET.SubElement(issue, 'notes').text = result['text']


def parse_issues(resultpath):
    ''' Convert rpmlint results to a list of issues. '''
    with open(resultpath) as f:
        lines = f.readlines()
    issues = []
    for line in [l for l in lines if ':' in l]:
        issue = {}
        issue['path'], line = line.split(':', 1)
        nr_or_severity, line = line.split(':', 1)
        if re.search('[0-9]+', nr_or_severity):
             issue['line'] = nr_or_severity
        else:
             issue['severity'] = nr_or_severity
        if not 'severity' in issue:
             issue['severity'], line = line.split(':', 1)
        line = line.strip()
        if ' ' in line:
            issue['name'], issue['text'] = line.split(' ', 1)
        else:
            issue['name'], issue['text'] = line, ''
        for key in issue.iterkeys():
            issue[key] = issue[key].strip()
        issues.append(dict(issue))
    return issues


if __name__ == "__main__":
    if len(sys.argv) == 2 and sys.argv[1] == '-h' or sys.argv[1] == '--help':
        print sys.modules[__name__].__doc__
        sys.exit(0)
    elif len(sys.argv) != 3:
        print sys.modules[__name__].__doc__
        sys.exit(1)
    specpath = sys.argv[1]
    resultpath = sys.argv[2]

    lintversion = get_version()
    nvr = parse_spec_nvr(specpath)
    specpath, cs = get_specfile(specpath)
    issues = parse_issues(resultpath)
    descriptions = {}

    root = create_xmltree(specpath, nvr, cs, lintversion)
    for issue in issues:
        add_xml_result(root, issue, descriptions)
    dom = xml.dom.minidom.parseString(ET.tostring(root))
    sys.stdout.write(dom.toprettyxml(indent='    '))


# vim: set expandtab ts=4 sw=4:


More information about the firehose-devel mailing list