summaryrefslogtreecommitdiff
path: root/chromium/build/android/lint/suppress.py
blob: 5e36d28963eb1217ca28916483f2d5f06226213a (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
#!/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()