Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(127)

Unified Diff: tools/grit/grit/node/misc.py

Issue 1410853008: Move grit from DEPS into src. (Closed) Base URL: https://chromium.googlesource.com/chromium/src.git@master
Patch Set: webview licenses Created 5 years, 1 month ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View side-by-side diff with in-line comments
Download patch
« no previous file with comments | « tools/grit/grit/node/message_unittest.py ('k') | tools/grit/grit/node/misc_unittest.py » ('j') | no next file with comments »
Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
Index: tools/grit/grit/node/misc.py
diff --git a/tools/grit/grit/node/misc.py b/tools/grit/grit/node/misc.py
new file mode 100755
index 0000000000000000000000000000000000000000..bd999709bdc58275e36997797f30b997391f87f1
--- /dev/null
+++ b/tools/grit/grit/node/misc.py
@@ -0,0 +1,552 @@
+#!/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.
+
+"""Miscellaneous node types.
+"""
+
+import os.path
+import re
+import sys
+
+from grit import constants
+from grit import exception
+from grit import util
+import grit.format.rc_header
+from grit.node import base
+from grit.node import io
+from grit.node import message
+
+
+# RTL languages
+# TODO(jennyz): remove this fixed set of RTL language array
+# now that generic expand_variable code exists.
+_RTL_LANGS = (
+ 'ar', # Arabic
+ 'fa', # Farsi
+ 'iw', # Hebrew
+ 'ks', # Kashmiri
+ 'ku', # Kurdish
+ 'ps', # Pashto
+ 'ur', # Urdu
+ 'yi', # Yiddish
+)
+
+
+def _ReadFirstIdsFromFile(filename, defines):
+ """Read the starting resource id values from |filename|. We also
+ expand variables of the form <(FOO) based on defines passed in on
+ the command line.
+
+ Returns a tuple, the absolute path of SRCDIR followed by the
+ first_ids dictionary.
+ """
+ first_ids_dict = eval(util.ReadFile(filename, util.RAW_TEXT))
+ src_root_dir = os.path.abspath(os.path.join(os.path.dirname(filename),
+ first_ids_dict['SRCDIR']))
+
+ def ReplaceVariable(matchobj):
+ for key, value in defines.iteritems():
+ if matchobj.group(1) == key:
+ return value
+ return ''
+
+ renames = []
+ for grd_filename in first_ids_dict:
+ new_grd_filename = re.sub(r'<\(([A-Za-z_]+)\)', ReplaceVariable,
+ grd_filename)
+ if new_grd_filename != grd_filename:
+ abs_grd_filename = os.path.abspath(new_grd_filename)
+ if abs_grd_filename[:len(src_root_dir)] != src_root_dir:
+ new_grd_filename = os.path.basename(abs_grd_filename)
+ else:
+ new_grd_filename = abs_grd_filename[len(src_root_dir) + 1:]
+ new_grd_filename = new_grd_filename.replace('\\', '/')
+ renames.append((grd_filename, new_grd_filename))
+
+ for grd_filename, new_grd_filename in renames:
+ first_ids_dict[new_grd_filename] = first_ids_dict[grd_filename]
+ del(first_ids_dict[grd_filename])
+
+ return (src_root_dir, first_ids_dict)
+
+
+class SplicingNode(base.Node):
+ """A node whose children should be considered to be at the same level as
+ its siblings for most purposes. This includes <if> and <part> nodes.
+ """
+
+ def _IsValidChild(self, child):
+ assert self.parent, '<%s> node should never be root.' % self.name
+ if isinstance(child, SplicingNode):
+ return True # avoid O(n^2) behavior
+ return self.parent._IsValidChild(child)
+
+
+class IfNode(SplicingNode):
+ """A node for conditional inclusion of resources.
+ """
+
+ def MandatoryAttributes(self):
+ return ['expr']
+
+ def _IsValidChild(self, child):
+ return (isinstance(child, (ThenNode, ElseNode)) or
+ super(IfNode, self)._IsValidChild(child))
+
+ def EndParsing(self):
+ children = self.children
+ self.if_then_else = False
+ if any(isinstance(node, (ThenNode, ElseNode)) for node in children):
+ if (len(children) != 2 or not isinstance(children[0], ThenNode) or
+ not isinstance(children[1], ElseNode)):
+ raise exception.UnexpectedChild(
+ '<if> element must be <if><then>...</then><else>...</else></if>')
+ self.if_then_else = True
+
+ def ActiveChildren(self):
+ cond = self.EvaluateCondition(self.attrs['expr'])
+ if self.if_then_else:
+ return self.children[0 if cond else 1].ActiveChildren()
+ else:
+ # Equivalent to having all children inside <then> with an empty <else>
+ return super(IfNode, self).ActiveChildren() if cond else []
+
+
+class ThenNode(SplicingNode):
+ """A <then> node. Can only appear directly inside an <if> node."""
+ pass
+
+
+class ElseNode(SplicingNode):
+ """An <else> node. Can only appear directly inside an <if> node."""
+ pass
+
+
+class PartNode(SplicingNode):
+ """A node for inclusion of sub-grd (*.grp) files.
+ """
+
+ def __init__(self):
+ super(PartNode, self).__init__()
+ self.started_inclusion = False
+
+ def MandatoryAttributes(self):
+ return ['file']
+
+ def _IsValidChild(self, child):
+ return self.started_inclusion and super(PartNode, self)._IsValidChild(child)
+
+
+class ReleaseNode(base.Node):
+ """The <release> element."""
+
+ def _IsValidChild(self, child):
+ from grit.node import empty
+ return isinstance(child, (empty.IncludesNode, empty.MessagesNode,
+ empty.StructuresNode, empty.IdentifiersNode))
+
+ def _IsValidAttribute(self, name, value):
+ return (
+ (name == 'seq' and int(value) <= self.GetRoot().GetCurrentRelease()) or
+ name == 'allow_pseudo'
+ )
+
+ def MandatoryAttributes(self):
+ return ['seq']
+
+ def DefaultAttributes(self):
+ return { 'allow_pseudo' : 'true' }
+
+ def GetReleaseNumber():
+ """Returns the sequence number of this release."""
+ return self.attribs['seq']
+
+class GritNode(base.Node):
+ """The <grit> root element."""
+
+ def __init__(self):
+ super(GritNode, self).__init__()
+ self.output_language = ''
+ self.defines = {}
+ self.substituter = None
+ self.target_platform = sys.platform
+
+ def _IsValidChild(self, child):
+ from grit.node import empty
+ return isinstance(child, (ReleaseNode, empty.TranslationsNode,
+ empty.OutputsNode))
+
+ def _IsValidAttribute(self, name, value):
+ if name not in ['base_dir', 'first_ids_file', 'source_lang_id',
+ 'latest_public_release', 'current_release',
+ 'enc_check', 'tc_project', 'grit_version',
+ 'output_all_resource_defines', 'rc_header_format']:
+ return False
+ if name in ['latest_public_release', 'current_release'] and value.strip(
+ '0123456789') != '':
+ return False
+ return True
+
+ def MandatoryAttributes(self):
+ return ['latest_public_release', 'current_release']
+
+ def DefaultAttributes(self):
+ return {
+ 'base_dir' : '.',
+ 'first_ids_file': '',
+ 'grit_version': 1,
+ 'source_lang_id' : 'en',
+ 'enc_check' : constants.ENCODING_CHECK,
+ 'tc_project' : 'NEED_TO_SET_tc_project_ATTRIBUTE',
+ 'output_all_resource_defines': 'true',
+ 'rc_header_format': None
+ }
+
+ def EndParsing(self):
+ super(GritNode, self).EndParsing()
+ if (int(self.attrs['latest_public_release'])
+ > int(self.attrs['current_release'])):
+ raise exception.Parsing('latest_public_release cannot have a greater '
+ 'value than current_release')
+
+ self.ValidateUniqueIds()
+
+ # Add the encoding check if it's not present (should ensure that it's always
+ # present in all .grd files generated by GRIT). If it's present, assert if
+ # it's not correct.
+ if 'enc_check' not in self.attrs or self.attrs['enc_check'] == '':
+ self.attrs['enc_check'] = constants.ENCODING_CHECK
+ else:
+ assert self.attrs['enc_check'] == constants.ENCODING_CHECK, (
+ 'Are you sure your .grd file is in the correct encoding (UTF-8)?')
+
+ def ValidateUniqueIds(self):
+ """Validate that 'name' attribute is unique in all nodes in this tree
+ except for nodes that are children of <if> nodes.
+ """
+ unique_names = {}
+ duplicate_names = []
+ # To avoid false positives from mutually exclusive <if> clauses, check
+ # against whatever the output condition happens to be right now.
+ # TODO(benrg): do something better.
+ for node in self.ActiveDescendants():
+ if node.attrs.get('generateid', 'true') == 'false':
+ continue # Duplication not relevant in that case
+
+ for node_id in node.GetTextualIds():
+ if util.SYSTEM_IDENTIFIERS.match(node_id):
+ continue # predefined IDs are sometimes used more than once
+
+ if node_id in unique_names and node_id not in duplicate_names:
+ duplicate_names.append(node_id)
+ unique_names[node_id] = 1
+
+ if len(duplicate_names):
+ raise exception.DuplicateKey(', '.join(duplicate_names))
+
+
+ def GetCurrentRelease(self):
+ """Returns the current release number."""
+ return int(self.attrs['current_release'])
+
+ def GetLatestPublicRelease(self):
+ """Returns the latest public release number."""
+ return int(self.attrs['latest_public_release'])
+
+ def GetSourceLanguage(self):
+ """Returns the language code of the source language."""
+ return self.attrs['source_lang_id']
+
+ def GetTcProject(self):
+ """Returns the name of this project in the TranslationConsole, or
+ 'NEED_TO_SET_tc_project_ATTRIBUTE' if it is not defined."""
+ return self.attrs['tc_project']
+
+ def SetOwnDir(self, dir):
+ """Informs the 'grit' element of the directory the file it is in resides.
+ This allows it to calculate relative paths from the input file, which is
+ what we desire (rather than from the current path).
+
+ Args:
+ dir: r'c:\bla'
+
+ Return:
+ None
+ """
+ assert dir
+ self.base_dir = os.path.normpath(os.path.join(dir, self.attrs['base_dir']))
+
+ def GetBaseDir(self):
+ """Returns the base directory, relative to the working directory. To get
+ the base directory as set in the .grd file, use GetOriginalBaseDir()
+ """
+ if hasattr(self, 'base_dir'):
+ return self.base_dir
+ else:
+ return self.GetOriginalBaseDir()
+
+ def GetOriginalBaseDir(self):
+ """Returns the base directory, as set in the .grd file.
+ """
+ return self.attrs['base_dir']
+
+ def SetShouldOutputAllResourceDefines(self, value):
+ """Overrides the value of output_all_resource_defines found in the grd file.
+ """
+ self.attrs['output_all_resource_defines'] = 'true' if value else 'false'
+
+ def ShouldOutputAllResourceDefines(self):
+ """Returns true if all resource defines should be output, false if
+ defines for resources not emitted to resource files should be
+ skipped.
+ """
+ return self.attrs['output_all_resource_defines'] == 'true'
+
+ def GetRcHeaderFormat(self):
+ return self.attrs['rc_header_format']
+
+ def AssignRcHeaderFormat(self, rc_header_format):
+ self.attrs['rc_header_format'] = rc_header_format
+
+ def GetInputFiles(self):
+ """Returns the list of files that are read to produce the output."""
+
+ # Importing this here avoids a circular dependency in the imports.
+ # pylint: disable-msg=C6204
+ from grit.node import include
+ from grit.node import misc
+ from grit.node import structure
+ from grit.node import variant
+
+ # Check if the input is required for any output configuration.
+ input_files = set()
+ old_output_language = self.output_language
+ for lang, ctx, fallback in self.GetConfigurations():
+ self.SetOutputLanguage(lang or self.GetSourceLanguage())
+ self.SetOutputContext(ctx)
+ self.SetFallbackToDefaultLayout(fallback)
+
+ for node in self.ActiveDescendants():
+ if isinstance(node, (io.FileNode, include.IncludeNode, misc.PartNode,
+ structure.StructureNode, variant.SkeletonNode)):
+ input_path = node.GetInputPath()
+ if input_path is not None:
+ input_files.add(self.ToRealPath(input_path))
+
+ # If it's a flattened node, grab inlined resources too.
+ if ((node.name == 'structure' or node.name == 'include')
+ and node.attrs['flattenhtml'] == 'true'):
+ if node.name == 'structure':
+ node.RunPreSubstitutionGatherer()
+ input_files.update(node.GetHtmlResourceFilenames())
+
+ self.SetOutputLanguage(old_output_language)
+ return sorted(input_files)
+
+ def GetFirstIdsFile(self):
+ """Returns a usable path to the first_ids file, if set, otherwise
+ returns None.
+
+ The first_ids_file attribute is by default relative to the
+ base_dir of the .grd file, but may be prefixed by GRIT_DIR/,
+ which makes it relative to the directory of grit.py
+ (e.g. GRIT_DIR/../gritsettings/resource_ids).
+ """
+ if not self.attrs['first_ids_file']:
+ return None
+
+ path = self.attrs['first_ids_file']
+ GRIT_DIR_PREFIX = 'GRIT_DIR'
+ if (path.startswith(GRIT_DIR_PREFIX)
+ and path[len(GRIT_DIR_PREFIX)] in ['/', '\\']):
+ return util.PathFromRoot(path[len(GRIT_DIR_PREFIX) + 1:])
+ else:
+ return self.ToRealPath(path)
+
+ def GetOutputFiles(self):
+ """Returns the list of <output> nodes that are descendants of this node's
+ <outputs> child and are not enclosed by unsatisfied <if> conditionals.
+ """
+ for child in self.children:
+ if child.name == 'outputs':
+ return [node for node in child.ActiveDescendants()
+ if node.name == 'output']
+ raise exception.MissingElement()
+
+ def GetConfigurations(self):
+ """Returns the distinct (language, context, fallback_to_default_layout)
+ triples from the output nodes.
+ """
+ return set((n.GetLanguage(), n.GetContext(), n.GetFallbackToDefaultLayout()) for n in self.GetOutputFiles())
+
+ def GetSubstitutionMessages(self):
+ """Returns the list of <message sub_variable="true"> nodes."""
+ return [n for n in self.ActiveDescendants()
+ if isinstance(n, message.MessageNode)
+ and n.attrs['sub_variable'] == 'true']
+
+ def SetOutputLanguage(self, output_language):
+ """Set the output language. Prepares substitutions.
+
+ The substitutions are reset every time the language is changed.
+ They include messages designated as variables, and language codes for html
+ and rc files.
+
+ Args:
+ output_language: a two-letter language code (eg: 'en', 'ar'...) or ''
+ """
+ if not output_language:
+ # We do not specify the output language for .grh files,
+ # so we get an empty string as the default.
+ # The value should match grit.clique.MessageClique.source_language.
+ output_language = self.GetSourceLanguage()
+ if output_language != self.output_language:
+ self.output_language = output_language
+ self.substituter = None # force recalculate
+
+ def SetOutputContext(self, output_context):
+ self.output_context = output_context
+ self.substituter = None # force recalculate
+
+ def SetFallbackToDefaultLayout(self, fallback_to_default_layout):
+ self.fallback_to_default_layout = fallback_to_default_layout
+ self.substituter = None # force recalculate
+
+ def SetDefines(self, defines):
+ self.defines = defines
+ self.substituter = None # force recalculate
+
+ def SetTargetPlatform(self, target_platform):
+ self.target_platform = target_platform
+
+ def GetSubstituter(self):
+ if self.substituter is None:
+ self.substituter = util.Substituter()
+ self.substituter.AddMessages(self.GetSubstitutionMessages(),
+ self.output_language)
+ if self.output_language in _RTL_LANGS:
+ direction = 'dir="RTL"'
+ else:
+ direction = 'dir="LTR"'
+ self.substituter.AddSubstitutions({
+ 'GRITLANGCODE': self.output_language,
+ 'GRITDIR': direction,
+ })
+ from grit.format import rc # avoid circular dep
+ rc.RcSubstitutions(self.substituter, self.output_language)
+ return self.substituter
+
+ def AssignFirstIds(self, filename_or_stream, defines):
+ """Assign first ids to each grouping node based on values from the
+ first_ids file (if specified on the <grit> node).
+ """
+ # If the input is a stream, then we're probably in a unit test and
+ # should skip this step.
+ if type(filename_or_stream) not in (str, unicode):
+ return
+
+ # Nothing to do if the first_ids_filename attribute isn't set.
+ first_ids_filename = self.GetFirstIdsFile()
+ if not first_ids_filename:
+ return
+
+ src_root_dir, first_ids = _ReadFirstIdsFromFile(first_ids_filename,
+ defines)
+ from grit.node import empty
+ for node in self.Preorder():
+ if isinstance(node, empty.GroupingNode):
+ abs_filename = os.path.abspath(filename_or_stream)
+ if abs_filename[:len(src_root_dir)] != src_root_dir:
+ filename = os.path.basename(filename_or_stream)
+ else:
+ filename = abs_filename[len(src_root_dir) + 1:]
+ filename = filename.replace('\\', '/')
+
+ if node.attrs['first_id'] != '':
+ raise Exception(
+ "Don't set the first_id attribute when using the first_ids_file "
+ "attribute on the <grit> node, update %s instead." %
+ first_ids_filename)
+
+ try:
+ id_list = first_ids[filename][node.name]
+ except KeyError, e:
+ print '-' * 78
+ print 'Resource id not set for %s (%s)!' % (filename, node.name)
+ print ('Please update %s to include an entry for %s. See the '
+ 'comments in resource_ids for information on why you need to '
+ 'update that file.' % (first_ids_filename, filename))
+ print '-' * 78
+ raise e
+
+ try:
+ node.attrs['first_id'] = str(id_list.pop(0))
+ except IndexError, e:
+ raise Exception('Please update %s and add a first id for %s (%s).'
+ % (first_ids_filename, filename, node.name))
+
+ def RunGatherers(self, debug=False):
+ '''Call RunPreSubstitutionGatherer() on every node of the tree, then apply
+ substitutions, then call RunPostSubstitutionGatherer() on every node.
+
+ The substitutions step requires that the output language has been set.
+ Locally, get the Substitution messages and add them to the substituter.
+ Also add substitutions for language codes in the Rc.
+
+ Args:
+ debug: will print information while running gatherers.
+ '''
+ for node in self.ActiveDescendants():
+ if hasattr(node, 'RunPreSubstitutionGatherer'):
+ with node:
+ node.RunPreSubstitutionGatherer(debug=debug)
+
+ assert self.output_language
+ self.SubstituteMessages(self.GetSubstituter())
+
+ for node in self.ActiveDescendants():
+ if hasattr(node, 'RunPostSubstitutionGatherer'):
+ with node:
+ node.RunPostSubstitutionGatherer(debug=debug)
+
+
+class IdentifierNode(base.Node):
+ """A node for specifying identifiers that should appear in the resource
+ header file, and be unique amongst all other resource identifiers, but don't
+ have any other attributes or reference any resources.
+ """
+
+ def MandatoryAttributes(self):
+ return ['name']
+
+ def DefaultAttributes(self):
+ return { 'comment' : '', 'id' : '', 'systemid': 'false' }
+
+ def GetId(self):
+ """Returns the id of this identifier if it has one, None otherwise
+ """
+ if 'id' in self.attrs:
+ return self.attrs['id']
+ return None
+
+ def EndParsing(self):
+ """Handles system identifiers."""
+ super(IdentifierNode, self).EndParsing()
+ if self.attrs['systemid'] == 'true':
+ util.SetupSystemIdentifiers((self.attrs['name'],))
+
+ @staticmethod
+ def Construct(parent, name, id, comment, systemid='false'):
+ """Creates a new node which is a child of 'parent', with attributes set
+ by parameters of the same name.
+ """
+ node = IdentifierNode()
+ node.StartParsing('identifier', parent)
+ node.HandleAttribute('name', name)
+ node.HandleAttribute('id', id)
+ node.HandleAttribute('comment', comment)
+ node.HandleAttribute('systemid', systemid)
+ node.EndParsing()
+ return node
« no previous file with comments | « tools/grit/grit/node/message_unittest.py ('k') | tools/grit/grit/node/misc_unittest.py » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698