blob: 5e36d28963eb1217ca28916483f2d5f06226213a [file] [log] [blame]
#!/usr/bin/env python
#
# Copyright (c) 2013 The Chromium Authors. All rights reserved.
# Use of this source code is governed by a BSD-style license that can be
# found in the LICENSE file.
"""Add all generated lint_result.xml files to suppressions.xml"""
# pylint: disable=no-member
from __future__ import print_function
import argparse
import os
import re
import sys
from xml.dom import minidom
_BUILD_ANDROID_DIR = os.path.join(os.path.dirname(__file__), '..')
sys.path.append(_BUILD_ANDROID_DIR)
_TMP_DIR_RE = re.compile(r'^/tmp/.*/(SRC_ROOT[0-9]+|PRODUCT_DIR)/')
_THIS_FILE = os.path.abspath(__file__)
_DEFAULT_CONFIG_PATH = os.path.join(os.path.dirname(_THIS_FILE),
'suppressions.xml')
_INSERT_COMMENT = ('TODO: This line was added by suppress.py,'
' please add an explanation.')
class _Issue(object):
def __init__(self, dom_element):
self.regexps = set()
self.dom_element = dom_element
def _CollectIssuesFromDom(dom):
issues_dict = {}
for issue_element in dom.getElementsByTagName('issue'):
issue_id = issue_element.attributes['id'].value
issue = _Issue(issue_element)
issues_dict[issue_id] = issue
for child in issue_element.childNodes:
if child.nodeType != minidom.Node.ELEMENT_NODE:
continue
if child.tagName == 'ignore' and child.getAttribute('regexp'):
issue.regexps.add(child.getAttribute('regexp'))
return issues_dict
def _TrimWhitespaceNodes(n):
"""Remove all whitespace-only TEXT_NODEs."""
rm_children = []
for c in n.childNodes:
if c.nodeType == minidom.Node.TEXT_NODE and c.data.strip() == '':
rm_children.append(c)
else:
_TrimWhitespaceNodes(c)
for c in rm_children:
n.removeChild(c)
def _ParseAndInsertNewSuppressions(result_path, config_path):
print('Parsing %s' % config_path)
config_dom = minidom.parse(config_path)
issues_dict = _CollectIssuesFromDom(config_dom)
print('Parsing and merging %s' % result_path)
dom = minidom.parse(result_path)
for issue_element in dom.getElementsByTagName('issue'):
issue_id = issue_element.attributes['id'].value
severity = issue_element.attributes['severity'].value
path = issue_element.getElementsByTagName(
'location')[0].attributes['file'].value
# Strip temporary file path.
path = re.sub(_TMP_DIR_RE, '', path)
# Escape Java inner class name separator and suppress with regex instead
# of path. Doesn't use re.escape() as it is a bit too aggressive and
# escapes '_', causing trouble with PRODUCT_DIR.
regexp = path.replace('$', r'\$')
if issue_id not in issues_dict:
element = config_dom.createElement('issue')
element.attributes['id'] = issue_id
element.attributes['severity'] = severity
config_dom.documentElement.appendChild(element)
issue = _Issue(element)
issues_dict[issue_id] = issue
else:
issue = issues_dict[issue_id]
if issue.dom_element.getAttribute('severity') == 'ignore':
continue
if regexp not in issue.regexps:
issue.regexps.add(regexp)
ignore_element = config_dom.createElement('ignore')
ignore_element.attributes['regexp'] = regexp
issue.dom_element.appendChild(config_dom.createComment(_INSERT_COMMENT))
issue.dom_element.appendChild(ignore_element)
for issue_id, issue in issues_dict.iteritems():
if issue.dom_element.getAttribute('severity') == 'ignore':
print('Warning: [%s] is suppressed globally.' % issue_id)
# toprettyxml inserts whitespace, so delete whitespace first.
_TrimWhitespaceNodes(config_dom.documentElement)
with open(config_path, 'w') as f:
f.write(config_dom.toprettyxml(indent=' ', encoding='utf-8'))
print('Updated %s' % config_path)
def _Suppress(config_path, result_path):
_ParseAndInsertNewSuppressions(result_path, config_path)
def main():
parser = argparse.ArgumentParser(description=__doc__)
parser.add_argument('--config',
help='Path to suppression.xml config file',
default=_DEFAULT_CONFIG_PATH)
parser.add_argument('result_path',
help='Lint results xml file',
metavar='RESULT_FILE')
args = parser.parse_args()
_Suppress(args.config, args.result_path)
if __name__ == '__main__':
main()