1a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)#!/usr/bin/env python
2a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)#
3a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)# Copyright (c) 2013 The Chromium Authors. All rights reserved.
4a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)# Use of this source code is governed by a BSD-style license that can be
5a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)# found in the LICENSE file.
6a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)
7a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)"""Add all generated lint_result.xml files to suppressions.xml"""
8a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)
9a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)
10a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)import collections
11a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)import optparse
12a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)import os
13a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)import sys
14a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)from xml.dom import minidom
15a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)
16a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)_BUILD_ANDROID_DIR = os.path.join(os.path.dirname(__file__), '..')
17a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)sys.path.append(_BUILD_ANDROID_DIR)
18a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)
19a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)from pylib import constants
20a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)
21a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)
22a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)_THIS_FILE = os.path.abspath(__file__)
23a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)_CONFIG_PATH = os.path.join(os.path.dirname(_THIS_FILE), 'suppressions.xml')
24a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)_DOC = (
25a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)    '\nSTOP! It looks like you want to suppress some lint errors:\n'
26a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)    '- Have you tried identifing the offending patch?\n'
27a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)    '  Ask the author for a fix and/or revert the patch.\n'
28a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)    '- It is preferred to add suppressions in the code instead of\n'
29a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)    '  sweeping it under the rug here. See:\n\n'
30a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)    '    http://developer.android.com/tools/debugging/improving-w-lint.html\n'
31a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)    '\n'
32a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)    'Still reading?\n'
33a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)    '- You can edit this file manually to suppress an issue\n'
34a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)    '  globally if it is not applicable to the project.\n'
35a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)    '- You can also automatically add issues found so for in the\n'
36a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)    '  build process by running:\n\n'
37a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)    '    ' + os.path.relpath(_THIS_FILE, constants.DIR_SOURCE_ROOT) + '\n\n'
38a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)    '  which will generate this file (Comments are not preserved).\n'
39a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)    '  Note: PRODUCT_DIR will be substituted at run-time with actual\n'
40a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)    '  directory path (e.g. out/Debug)\n'
41a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles))
42a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)
43a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)
44a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)_Issue = collections.namedtuple('Issue', ['severity', 'paths'])
45a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)
46a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)
47a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)def _ParseConfigFile(config_path):
48a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)  print 'Parsing %s' % config_path
49a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)  issues_dict = {}
50a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)  dom = minidom.parse(config_path)
51a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)  for issue in dom.getElementsByTagName('issue'):
52a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)    issue_id = issue.attributes['id'].value
53a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)    severity = issue.getAttribute('severity')
54a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)    paths = set(
55a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)        [p.attributes['path'].value for p in
56a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)         issue.getElementsByTagName('ignore')])
57a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)    issues_dict[issue_id] = _Issue(severity, paths)
58a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)  return issues_dict
59a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)
60a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)
61a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)def _ParseAndMergeResultFile(result_path, issues_dict):
62a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)  print 'Parsing and merging %s' % result_path
63a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)  dom = minidom.parse(result_path)
64a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)  for issue in dom.getElementsByTagName('issue'):
65a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)    issue_id = issue.attributes['id'].value
66a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)    severity = issue.attributes['severity'].value
67a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)    path = issue.getElementsByTagName('location')[0].attributes['file'].value
68a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)    if issue_id not in issues_dict:
69a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)      issues_dict[issue_id] = _Issue(severity, set())
70a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)    issues_dict[issue_id].paths.add(path)
71a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)
72a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)
73a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)def _WriteConfigFile(config_path, issues_dict):
74a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)  new_dom = minidom.getDOMImplementation().createDocument(None, 'lint', None)
75a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)  top_element = new_dom.documentElement
76a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)  top_element.appendChild(new_dom.createComment(_DOC))
77a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)  for issue_id in sorted(issues_dict.keys()):
78a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)    severity = issues_dict[issue_id].severity
79a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)    paths = issues_dict[issue_id].paths
80a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)    issue = new_dom.createElement('issue')
81a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)    issue.attributes['id'] = issue_id
82a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)    if severity:
83a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)      issue.attributes['severity'] = severity
84a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)    if severity == 'ignore':
85a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)      print 'Warning: [%s] is suppressed globally.' % issue_id
86a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)    else:
87a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)      for path in sorted(paths):
88a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)        ignore = new_dom.createElement('ignore')
89a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)        ignore.attributes['path'] = path
90a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)        issue.appendChild(ignore)
91a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)    top_element.appendChild(issue)
92a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)
93a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)  with open(config_path, 'w') as f:
94a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)    f.write(new_dom.toprettyxml(indent='  ', encoding='utf-8'))
95a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)  print 'Updated %s' % config_path
96a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)
97a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)
98a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)def _Suppress(config_path, result_path):
99a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)  issues_dict = _ParseConfigFile(config_path)
100a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)  _ParseAndMergeResultFile(result_path, issues_dict)
101a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)  _WriteConfigFile(config_path, issues_dict)
102a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)
103a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)
104effb81e5f8246d0db0270817048dc992db66e9fbBen Murdochdef main():
105a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)  parser = optparse.OptionParser(usage='%prog RESULT-FILE')
106a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)  _, args = parser.parse_args()
107a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)
108a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)  if len(args) != 1 or not os.path.exists(args[0]):
109a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)    parser.error('Must provide RESULT-FILE')
110a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)
111a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)  _Suppress(_CONFIG_PATH, args[0])
112a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)
113a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)
114a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)if __name__ == '__main__':
115effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch  main()
116