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 9from grit import exception 10from grit import util 11from grit.extern import FP 12 13 14def 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 38def 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 82def 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 96def _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 205