| Index: tools/grit/grit/format/rc_header.py
|
| diff --git a/tools/grit/grit/format/rc_header.py b/tools/grit/grit/format/rc_header.py
|
| new file mode 100755
|
| index 0000000000000000000000000000000000000000..74e7127b5840b57a90aa022737c721f638280b33
|
| --- /dev/null
|
| +++ b/tools/grit/grit/format/rc_header.py
|
| @@ -0,0 +1,204 @@
|
| +#!/usr/bin/env python
|
| +# Copyright (c) 2012 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.
|
| +
|
| +'''Item formatters for RC headers.
|
| +'''
|
| +
|
| +from grit import exception
|
| +from grit import util
|
| +from grit.extern import FP
|
| +
|
| +
|
| +def Format(root, lang='en', output_dir='.'):
|
| + yield '''\
|
| +// This file is automatically generated by GRIT. Do not edit.
|
| +
|
| +#pragma once
|
| +'''
|
| + # Check for emit nodes under the rc_header. If any emit node
|
| + # is present, we assume it means the GRD file wants to override
|
| + # the default header, with no includes.
|
| + default_includes = ['#include <atlres.h>', '']
|
| + emit_lines = []
|
| + for output_node in root.GetOutputFiles():
|
| + if output_node.GetType() == 'rc_header':
|
| + for child in output_node.children:
|
| + if child.name == 'emit' and child.attrs['emit_type'] == 'prepend':
|
| + emit_lines.append(child.GetCdata())
|
| + for line in emit_lines or default_includes:
|
| + yield line + '\n'
|
| +
|
| + for line in FormatDefines(root, root.ShouldOutputAllResourceDefines(),
|
| + root.GetRcHeaderFormat()):
|
| + yield line
|
| +
|
| +
|
| +def FormatDefines(root, output_all_resource_defines=True,
|
| + rc_header_format=None):
|
| + '''Yields #define SYMBOL 1234 lines.
|
| +
|
| + Args:
|
| + root: A GritNode.
|
| + output_all_resource_defines: If False, output only the symbols used in the
|
| + current output configuration.
|
| + '''
|
| + from grit.node import message
|
| + tids = GetIds(root)
|
| +
|
| + if output_all_resource_defines:
|
| + items = root.Preorder()
|
| + else:
|
| + items = root.ActiveDescendants()
|
| +
|
| + if not rc_header_format:
|
| + rc_header_format = "#define {textual_id} {numeric_id}"
|
| + rc_header_format += "\n"
|
| + seen = set()
|
| + for item in items:
|
| + if not isinstance(item, message.MessageNode):
|
| + with item:
|
| + for tid in item.GetTextualIds():
|
| + if tid in tids and tid not in seen:
|
| + seen.add(tid)
|
| + yield rc_header_format.format(textual_id=tid,numeric_id=tids[tid])
|
| +
|
| + # Temporarily mimic old behavior: MessageNodes were only output if active,
|
| + # even with output_all_resource_defines set. TODO(benrg): Remove this after
|
| + # fixing problems in the Chrome tree.
|
| + for item in root.ActiveDescendants():
|
| + if isinstance(item, message.MessageNode):
|
| + with item:
|
| + for tid in item.GetTextualIds():
|
| + if tid in tids and tid not in seen:
|
| + seen.add(tid)
|
| + yield rc_header_format.format(textual_id=tid,numeric_id=tids[tid])
|
| +
|
| +
|
| +_cached_ids = {}
|
| +
|
| +
|
| +def GetIds(root):
|
| + '''Return a dictionary mapping textual ids to numeric ids for the given tree.
|
| +
|
| + Args:
|
| + root: A GritNode.
|
| + '''
|
| + # TODO(benrg): Since other formatters use this, it might make sense to move it
|
| + # and _ComputeIds to GritNode and store the cached ids as an attribute. On the
|
| + # other hand, GritNode has too much random stuff already.
|
| + if root not in _cached_ids:
|
| + _cached_ids[root] = _ComputeIds(root)
|
| + return _cached_ids[root]
|
| +
|
| +
|
| +def _ComputeIds(root):
|
| + from grit.node import empty, include, message, misc, structure
|
| +
|
| + ids = {} # Maps numeric id to textual id
|
| + tids = {} # Maps textual id to numeric id
|
| + id_reasons = {} # Maps numeric id to text id and a human-readable explanation
|
| + group = None
|
| + last_id = None
|
| +
|
| + for item in root:
|
| + if isinstance(item, empty.GroupingNode):
|
| + # Note: this won't work if any GroupingNode can be contained inside
|
| + # another.
|
| + group = item
|
| + last_id = None
|
| + continue
|
| +
|
| + assert not item.GetTextualIds() or isinstance(item,
|
| + (include.IncludeNode, message.MessageNode,
|
| + misc.IdentifierNode, structure.StructureNode))
|
| +
|
| + # Resources that use the RES protocol don't need
|
| + # any numerical ids generated, so we skip them altogether.
|
| + # This is accomplished by setting the flag 'generateid' to false
|
| + # in the GRD file.
|
| + if item.attrs.get('generateid', 'true') == 'false':
|
| + continue
|
| +
|
| + for tid in item.GetTextualIds():
|
| + if util.SYSTEM_IDENTIFIERS.match(tid):
|
| + # Don't emit a new ID for predefined IDs
|
| + continue
|
| +
|
| + if tid in tids:
|
| + continue
|
| +
|
| + # Some identifier nodes can provide their own id,
|
| + # and we use that id in the generated header in that case.
|
| + if hasattr(item, 'GetId') and item.GetId():
|
| + id = long(item.GetId())
|
| + reason = 'returned by GetId() method'
|
| +
|
| + elif ('offset' in item.attrs and group and
|
| + group.attrs.get('first_id', '') != ''):
|
| + offset_text = item.attrs['offset']
|
| + parent_text = group.attrs['first_id']
|
| +
|
| + try:
|
| + offset_id = long(offset_text)
|
| + except ValueError:
|
| + offset_id = tids[offset_text]
|
| +
|
| + try:
|
| + parent_id = long(parent_text)
|
| + except ValueError:
|
| + parent_id = tids[parent_text]
|
| +
|
| + id = parent_id + offset_id
|
| + reason = 'first_id %d + offset %d' % (parent_id, offset_id)
|
| +
|
| + # We try to allocate IDs sequentially for blocks of items that might
|
| + # be related, for instance strings in a stringtable (as their IDs might be
|
| + # used e.g. as IDs for some radio buttons, in which case the IDs must
|
| + # be sequential).
|
| + #
|
| + # We do this by having the first item in a section store its computed ID
|
| + # (computed from a fingerprint) in its parent object. Subsequent children
|
| + # of the same parent will then try to get IDs that sequentially follow
|
| + # the currently stored ID (on the parent) and increment it.
|
| + elif last_id is None:
|
| + # First check if the starting ID is explicitly specified by the parent.
|
| + if group and group.attrs.get('first_id', '') != '':
|
| + id = long(group.attrs['first_id'])
|
| + reason = "from parent's first_id attribute"
|
| + else:
|
| + # Automatically generate the ID based on the first clique from the
|
| + # first child of the first child node of our parent (i.e. when we
|
| + # first get to this location in the code).
|
| +
|
| + # According to
|
| + # http://msdn.microsoft.com/en-us/library/t2zechd4(VS.71).aspx
|
| + # the safe usable range for resource IDs in Windows is from decimal
|
| + # 101 to 0x7FFF.
|
| +
|
| + id = FP.UnsignedFingerPrint(tid)
|
| + id = id % (0x7FFF - 101) + 101
|
| + reason = 'chosen by random fingerprint -- use first_id to override'
|
| +
|
| + last_id = id
|
| + else:
|
| + id = last_id = last_id + 1
|
| + reason = 'sequentially assigned'
|
| +
|
| + reason = "%s (%s)" % (tid, reason)
|
| + # Don't fail when 'offset' is specified, as the base and the 0th
|
| + # offset will have the same ID.
|
| + if id in id_reasons and not 'offset' in item.attrs:
|
| + raise exception.IdRangeOverlap('ID %d was assigned to both %s and %s.'
|
| + % (id, id_reasons[id], reason))
|
| +
|
| + if id < 101:
|
| + print ('WARNING: Numeric resource IDs should be greater than 100 to\n'
|
| + 'avoid conflicts with system-defined resource IDs.')
|
| +
|
| + ids[id] = tid
|
| + tids[tid] = id
|
| + id_reasons[id] = reason
|
| +
|
| + return tids
|
|
|