1 # Copyright 2014 The Chromium Authors. All rights reserved.
2 # Use of this source code is governed by a BSD-style license that can be
3 # found in the LICENSE file.
5 """Updates enums in histograms.xml file with values read from provided C++ enum.
7 If the file was pretty-printed, the updated version is pretty-printed too.
16 from xml.dom import minidom
18 sys.path.append(os.path.join(os.path.dirname(__file__), '..', 'common'))
19 from diff_util import PromptUserToAcceptDiff
21 class UserError(Exception):
22 def __init__(self, message):
23 Exception.__init__(self, message)
34 def ReadHistogramValues(filename, start_marker, end_marker):
35 """Reads in values from |filename|, returning a list of (label, value) pairs
36 corresponding to the enum framed by |start_marker| and |end_marker|.
38 # Read the file as a list of lines
39 with open(filename) as f:
40 content = f.readlines()
42 START_REGEX = re.compile(start_marker)
43 ITEM_REGEX = re.compile(r'^(\w+)')
44 ITEM_REGEX_WITH_INIT = re.compile(r'(\w+)\s*=\s*(\d+)')
45 END_REGEX = re.compile(end_marker)
47 # Locate the enum definition and collect all entries in it
48 inside_enum = False # We haven't found the enum definition yet
53 # Exit condition: we reached last enum value
54 if END_REGEX.match(line):
57 # Inside enum: generate new xml entry
58 m = ITEM_REGEX_WITH_INIT.match(line)
60 enum_value = int(m.group(2))
63 m = ITEM_REGEX.match(line)
68 result[enum_value] = label
71 if START_REGEX.match(line):
77 def CreateEnumItemNode(document, value, label):
78 """Creates an int element to append to an enum."""
79 item_node = document.createElement('int')
80 item_node.attributes['value'] = str(value)
81 item_node.attributes['label'] = label
85 def UpdateHistogramDefinitions(histogram_enum_name, source_enum_values,
86 source_enum_path, document):
87 """Updates the enum node named |histogram_enum_name| based on the definition
88 stored in |source_enum_values|. Existing items for which |source_enum_values|
89 doesn't contain any corresponding data will be preserved. |source_enum_path|
90 will be used to insert a comment.
92 # Get a dom of <enum name=|name| ...> node in |document|.
93 for enum_node in document.getElementsByTagName('enum'):
94 if enum_node.attributes['name'].value == histogram_enum_name:
97 raise UserError('No {0} enum node found'.format(name))
101 # Add a "Generated from (...)" comment.
103 document.createComment(' Generated from {0} '.format(source_enum_path)))
105 # Scan existing nodes in |enum_node| and build |new_children|.
106 # - For each int node in |enum_node|, if there is a corresponding entry in
107 # |source_enum_values|, drop the existing node and add a node newly created
108 # from |source_enum_values| to |new_children|.
109 # - Drop existing "Generated from (...)" comment in |enum_node|.
110 # - Copy anything else.
111 SOURCE_COMMENT_REGEX = re.compile('^ Generated from ')
112 for child in enum_node.childNodes:
113 if child.nodeName == 'int':
114 value = int(child.attributes['value'].value)
115 if source_enum_values.has_key(value):
117 CreateEnumItemNode(document, value, source_enum_values[value]))
118 del source_enum_values[value]
120 new_children.append(child)
121 # Drop existing source comments if any.
122 elif (child.nodeType != minidom.Node.COMMENT_NODE or
123 SOURCE_COMMENT_REGEX.match(child.data) is None):
124 new_children.append(child)
126 # Add remaining entries i.e. new enum values, in the |source_enum_values| to
127 # the |new_children|.
128 for value in sorted(source_enum_values.iterkeys()):
130 CreateEnumItemNode(document, value, source_enum_values[value]))
132 # Update |enum_node|.
133 while enum_node.hasChildNodes():
134 enum_node.removeChild(enum_node.lastChild)
135 for child in new_children:
136 enum_node.appendChild(child)
139 def UpdateHistogramEnum(histogram_enum_name, source_enum_path,
140 start_marker, end_marker):
141 """Updates |histogram_enum_name| enum in histograms.xml file with values
142 read from |source_enum_path|, where |start_marker| and |end_marker| indicate
143 the beginning and end of the source enum definition, respectively.
145 # TODO(ahernandez.miralles): The line below is present in nearly every
146 # file in this directory; factor out into a central location
147 HISTOGRAMS_PATH = 'histograms.xml'
149 Log('Reading histogram enum definition from "{0}".'.format(source_enum_path))
150 source_enum_values = ReadHistogramValues(source_enum_path, start_marker,
153 Log('Reading existing histograms from "{0}".'.format(HISTOGRAMS_PATH))
154 with open(HISTOGRAMS_PATH, 'rb') as f:
155 histograms_doc = minidom.parse(f)
159 Log('Comparing histograms enum with new enum definition.')
160 UpdateHistogramDefinitions(histogram_enum_name, source_enum_values,
161 source_enum_path, histograms_doc)
163 Log('Writing out new histograms file.')
164 new_xml = print_style.GetPrintStyle().PrettyPrintNode(histograms_doc)
165 if not PromptUserToAcceptDiff(
166 xml, new_xml, 'Is the updated version acceptable?'):
170 with open(HISTOGRAMS_PATH, 'wb') as f: