| OLD | NEW |
| (Empty) |
| 1 #!/usr/bin/env python | |
| 2 # Copyright (c) 2012 The Chromium Authors. All rights reserved. | |
| 3 # Use of this source code is governed by a BSD-style license that can be | |
| 4 # found in the LICENSE file. | |
| 5 | |
| 6 '''Item formatters for RC headers. | |
| 7 ''' | |
| 8 | |
| 9 from grit import exception | |
| 10 from grit import util | |
| 11 from grit.extern import FP | |
| 12 | |
| 13 | |
| 14 def Format(root, lang='en', output_dir='.'): | |
| 15 yield '''\ | |
| 16 // This file is automatically generated by GRIT. Do not edit. | |
| 17 | |
| 18 #pragma once | |
| 19 ''' | |
| 20 # Check for emit nodes under the rc_header. If any emit node | |
| 21 # is present, we assume it means the GRD file wants to override | |
| 22 # the default header, with no includes. | |
| 23 default_includes = ['#include <atlres.h>', ''] | |
| 24 emit_lines = [] | |
| 25 for output_node in root.GetOutputFiles(): | |
| 26 if output_node.GetType() == 'rc_header': | |
| 27 for child in output_node.children: | |
| 28 if child.name == 'emit' and child.attrs['emit_type'] == 'prepend': | |
| 29 emit_lines.append(child.GetCdata()) | |
| 30 for line in emit_lines or default_includes: | |
| 31 yield line + '\n' | |
| 32 | |
| 33 for line in FormatDefines(root, root.ShouldOutputAllResourceDefines(), | |
| 34 root.GetRcHeaderFormat()): | |
| 35 yield line | |
| 36 | |
| 37 | |
| 38 def FormatDefines(root, output_all_resource_defines=True, | |
| 39 rc_header_format=None): | |
| 40 '''Yields #define SYMBOL 1234 lines. | |
| 41 | |
| 42 Args: | |
| 43 root: A GritNode. | |
| 44 output_all_resource_defines: If False, output only the symbols used in the | |
| 45 current output configuration. | |
| 46 ''' | |
| 47 from grit.node import message | |
| 48 tids = GetIds(root) | |
| 49 | |
| 50 if output_all_resource_defines: | |
| 51 items = root.Preorder() | |
| 52 else: | |
| 53 items = root.ActiveDescendants() | |
| 54 | |
| 55 if not rc_header_format: | |
| 56 rc_header_format = "#define {textual_id} {numeric_id}" | |
| 57 rc_header_format += "\n" | |
| 58 seen = set() | |
| 59 for item in items: | |
| 60 if not isinstance(item, message.MessageNode): | |
| 61 with item: | |
| 62 for tid in item.GetTextualIds(): | |
| 63 if tid in tids and tid not in seen: | |
| 64 seen.add(tid) | |
| 65 yield rc_header_format.format(textual_id=tid,numeric_id=tids[tid]) | |
| 66 | |
| 67 # Temporarily mimic old behavior: MessageNodes were only output if active, | |
| 68 # even with output_all_resource_defines set. TODO(benrg): Remove this after | |
| 69 # fixing problems in the Chrome tree. | |
| 70 for item in root.ActiveDescendants(): | |
| 71 if isinstance(item, message.MessageNode): | |
| 72 with item: | |
| 73 for tid in item.GetTextualIds(): | |
| 74 if tid in tids and tid not in seen: | |
| 75 seen.add(tid) | |
| 76 yield rc_header_format.format(textual_id=tid,numeric_id=tids[tid]) | |
| 77 | |
| 78 | |
| 79 _cached_ids = {} | |
| 80 | |
| 81 | |
| 82 def GetIds(root): | |
| 83 '''Return a dictionary mapping textual ids to numeric ids for the given tree. | |
| 84 | |
| 85 Args: | |
| 86 root: A GritNode. | |
| 87 ''' | |
| 88 # TODO(benrg): Since other formatters use this, it might make sense to move it | |
| 89 # and _ComputeIds to GritNode and store the cached ids as an attribute. On the | |
| 90 # other hand, GritNode has too much random stuff already. | |
| 91 if root not in _cached_ids: | |
| 92 _cached_ids[root] = _ComputeIds(root) | |
| 93 return _cached_ids[root] | |
| 94 | |
| 95 | |
| 96 def _ComputeIds(root): | |
| 97 from grit.node import empty, include, message, misc, structure | |
| 98 | |
| 99 ids = {} # Maps numeric id to textual id | |
| 100 tids = {} # Maps textual id to numeric id | |
| 101 id_reasons = {} # Maps numeric id to text id and a human-readable explanation | |
| 102 group = None | |
| 103 last_id = None | |
| 104 | |
| 105 for item in root: | |
| 106 if isinstance(item, empty.GroupingNode): | |
| 107 # Note: this won't work if any GroupingNode can be contained inside | |
| 108 # another. | |
| 109 group = item | |
| 110 last_id = None | |
| 111 continue | |
| 112 | |
| 113 assert not item.GetTextualIds() or isinstance(item, | |
| 114 (include.IncludeNode, message.MessageNode, | |
| 115 misc.IdentifierNode, structure.StructureNode)) | |
| 116 | |
| 117 # Resources that use the RES protocol don't need | |
| 118 # any numerical ids generated, so we skip them altogether. | |
| 119 # This is accomplished by setting the flag 'generateid' to false | |
| 120 # in the GRD file. | |
| 121 if item.attrs.get('generateid', 'true') == 'false': | |
| 122 continue | |
| 123 | |
| 124 for tid in item.GetTextualIds(): | |
| 125 if util.SYSTEM_IDENTIFIERS.match(tid): | |
| 126 # Don't emit a new ID for predefined IDs | |
| 127 continue | |
| 128 | |
| 129 if tid in tids: | |
| 130 continue | |
| 131 | |
| 132 # Some identifier nodes can provide their own id, | |
| 133 # and we use that id in the generated header in that case. | |
| 134 if hasattr(item, 'GetId') and item.GetId(): | |
| 135 id = long(item.GetId()) | |
| 136 reason = 'returned by GetId() method' | |
| 137 | |
| 138 elif ('offset' in item.attrs and group and | |
| 139 group.attrs.get('first_id', '') != ''): | |
| 140 offset_text = item.attrs['offset'] | |
| 141 parent_text = group.attrs['first_id'] | |
| 142 | |
| 143 try: | |
| 144 offset_id = long(offset_text) | |
| 145 except ValueError: | |
| 146 offset_id = tids[offset_text] | |
| 147 | |
| 148 try: | |
| 149 parent_id = long(parent_text) | |
| 150 except ValueError: | |
| 151 parent_id = tids[parent_text] | |
| 152 | |
| 153 id = parent_id + offset_id | |
| 154 reason = 'first_id %d + offset %d' % (parent_id, offset_id) | |
| 155 | |
| 156 # We try to allocate IDs sequentially for blocks of items that might | |
| 157 # be related, for instance strings in a stringtable (as their IDs might be | |
| 158 # used e.g. as IDs for some radio buttons, in which case the IDs must | |
| 159 # be sequential). | |
| 160 # | |
| 161 # We do this by having the first item in a section store its computed ID | |
| 162 # (computed from a fingerprint) in its parent object. Subsequent children | |
| 163 # of the same parent will then try to get IDs that sequentially follow | |
| 164 # the currently stored ID (on the parent) and increment it. | |
| 165 elif last_id is None: | |
| 166 # First check if the starting ID is explicitly specified by the parent. | |
| 167 if group and group.attrs.get('first_id', '') != '': | |
| 168 id = long(group.attrs['first_id']) | |
| 169 reason = "from parent's first_id attribute" | |
| 170 else: | |
| 171 # Automatically generate the ID based on the first clique from the | |
| 172 # first child of the first child node of our parent (i.e. when we | |
| 173 # first get to this location in the code). | |
| 174 | |
| 175 # According to | |
| 176 # http://msdn.microsoft.com/en-us/library/t2zechd4(VS.71).aspx | |
| 177 # the safe usable range for resource IDs in Windows is from decimal | |
| 178 # 101 to 0x7FFF. | |
| 179 | |
| 180 id = FP.UnsignedFingerPrint(tid) | |
| 181 id = id % (0x7FFF - 101) + 101 | |
| 182 reason = 'chosen by random fingerprint -- use first_id to override' | |
| 183 | |
| 184 last_id = id | |
| 185 else: | |
| 186 id = last_id = last_id + 1 | |
| 187 reason = 'sequentially assigned' | |
| 188 | |
| 189 reason = "%s (%s)" % (tid, reason) | |
| 190 # Don't fail when 'offset' is specified, as the base and the 0th | |
| 191 # offset will have the same ID. | |
| 192 if id in id_reasons and not 'offset' in item.attrs: | |
| 193 raise exception.IdRangeOverlap('ID %d was assigned to both %s and %s.' | |
| 194 % (id, id_reasons[id], reason)) | |
| 195 | |
| 196 if id < 101: | |
| 197 print ('WARNING: Numeric resource IDs should be greater than 100 to\n' | |
| 198 'avoid conflicts with system-defined resource IDs.') | |
| 199 | |
| 200 ids[id] = tid | |
| 201 tids[tid] = id | |
| 202 id_reasons[id] = reason | |
| 203 | |
| 204 return tids | |
| OLD | NEW |