| OLD | NEW |
| 1 #!/usr/bin/env python | 1 #!/usr/bin/env python |
| 2 # Copyright (c) 2012 The Chromium Authors. All rights reserved. | 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 | 3 # Use of this source code is governed by a BSD-style license that can be |
| 4 # found in the LICENSE file. | 4 # found in the LICENSE file. |
| 5 | 5 |
| 6 '''Base types for nodes in a GRIT resource tree. | 6 '''Base types for nodes in a GRIT resource tree. |
| 7 ''' | 7 ''' |
| 8 | 8 |
| 9 import collections | 9 import ast |
| 10 import os | 10 import os |
| 11 import sys | |
| 12 import types | 11 import types |
| 13 from xml.sax import saxutils | 12 from xml.sax import saxutils |
| 14 | 13 |
| 15 from grit import clique | 14 from grit import clique |
| 16 from grit import exception | 15 from grit import exception |
| 17 from grit import util | 16 from grit import util |
| 18 | 17 |
| 19 | 18 |
| 20 class Node(object): | 19 class Node(object): |
| 21 '''An item in the tree that has children.''' | 20 '''An item in the tree that has children.''' |
| 22 | 21 |
| 23 # Valid content types that can be returned by _ContentType() | 22 # Valid content types that can be returned by _ContentType() |
| 24 _CONTENT_TYPE_NONE = 0 # No CDATA content but may have children | 23 _CONTENT_TYPE_NONE = 0 # No CDATA content but may have children |
| 25 _CONTENT_TYPE_CDATA = 1 # Only CDATA, no children. | 24 _CONTENT_TYPE_CDATA = 1 # Only CDATA, no children. |
| 26 _CONTENT_TYPE_MIXED = 2 # CDATA and children, possibly intermingled | 25 _CONTENT_TYPE_MIXED = 2 # CDATA and children, possibly intermingled |
| 27 | 26 |
| 28 # Default nodes to not whitelist skipped | 27 # Default nodes to not whitelist skipped |
| 29 _whitelist_marked_as_skip = False | 28 _whitelist_marked_as_skip = False |
| 30 | 29 |
| 31 # A class-static cache to memoize EvaluateExpression(). | 30 # A class-static cache to speed up EvaluateExpression(). |
| 32 # It has a 2 level nested dict structure. The outer dict has keys | 31 # Keys are expressions (e.g. 'is_ios and lang == "fr"'). Values are tuples |
| 33 # of tuples which define the environment in which the expression | 32 # (code, variables_in_expr) where code is the compiled expression and can be |
| 34 # will be evaluated. The inner dict is map of expr->result. | 33 # directly eval'd, and variables_in_expr is the list of variable and method |
| 35 eval_expr_cache = collections.defaultdict(dict) | 34 # names used in the expression (e.g. ['is_ios', 'lang']). |
| 35 eval_expr_cache = {} |
| 36 | 36 |
| 37 def __init__(self): | 37 def __init__(self): |
| 38 self.children = [] # A list of child elements | 38 self.children = [] # A list of child elements |
| 39 self.mixed_content = [] # A list of u'' and/or child elements (this | 39 self.mixed_content = [] # A list of u'' and/or child elements (this |
| 40 # duplicates 'children' but | 40 # duplicates 'children' but |
| 41 # is needed to preserve markup-type content). | 41 # is needed to preserve markup-type content). |
| 42 self.name = u'' # The name of this element | 42 self.name = u'' # The name of this element |
| 43 self.attrs = {} # The set of attributes (keys to values) | 43 self.attrs = {} # The set of attributes (keys to values) |
| 44 self.parent = None # Our parent unless we are the root element. | 44 self.parent = None # Our parent unless we are the root element. |
| 45 self.uberclique = None # Allows overriding uberclique for parts of tree | 45 self.uberclique = None # Allows overriding uberclique for parts of tree |
| (...skipping 389 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 435 return [child for child in self if isinstance(child, type)] | 435 return [child for child in self if isinstance(child, type)] |
| 436 | 436 |
| 437 def GetTextualIds(self): | 437 def GetTextualIds(self): |
| 438 '''Returns a list of the textual ids of this node. | 438 '''Returns a list of the textual ids of this node. |
| 439 ''' | 439 ''' |
| 440 if 'name' in self.attrs: | 440 if 'name' in self.attrs: |
| 441 return [self.attrs['name']] | 441 return [self.attrs['name']] |
| 442 return [] | 442 return [] |
| 443 | 443 |
| 444 @classmethod | 444 @classmethod |
| 445 def GetPlatformAssertion(cls, target_platform): | 445 def EvaluateExpression(cls, expr, defs, target_platform, extra_variables={}): |
| 446 '''If the platform is a specific well-known platform, this returns | 446 '''Worker for EvaluateCondition (below) and conditions in XTB files.''' |
| 447 the is_xyz string representing that platform (e.g. is_linux), | 447 if expr in cls.eval_expr_cache: |
| 448 otherwise the empty string. | 448 code, variables_in_expr = cls.eval_expr_cache[expr] |
| 449 ''' | 449 else: |
| 450 platform = '' | 450 # Get a list of all variable and method names used in the expression. |
| 451 if target_platform == 'darwin': | 451 syntax_tree = ast.parse(expr, mode='eval') |
| 452 platform = 'is_macosx' | 452 variables_in_expr = [node.id for node in ast.walk(syntax_tree) if |
| 453 elif target_platform.startswith('linux'): | 453 isinstance(node, ast.Name) and node.id not in ('True', 'False')] |
| 454 platform = 'is_linux' | 454 code = compile(syntax_tree, filename='<string>', mode='eval') |
| 455 elif target_platform in ('cygwin', 'win32'): | 455 cls.eval_expr_cache[expr] = code, variables_in_expr |
| 456 platform = 'is_win' | |
| 457 elif target_platform in ('android', 'ios'): | |
| 458 platform = 'is_%s' % target_platform | |
| 459 return platform | |
| 460 | 456 |
| 461 @classmethod | 457 # Set values only for variables that are needed to eval the expression. |
| 462 def EvaluateExpression(cls, expr, defs, target_platform, extra_variables=None)
: | 458 variable_map = {} |
| 463 '''Worker for EvaluateCondition (below) and conditions in XTB files.''' | 459 for name in variables_in_expr: |
| 464 cache_dict = cls.eval_expr_cache[ | 460 if name == 'os': |
| 465 (tuple(defs.iteritems()), target_platform, extra_variables)] | 461 value = target_platform |
| 466 if expr in cache_dict: | 462 elif name == 'defs': |
| 467 return cache_dict[expr] | 463 value = defs |
| 468 def pp_ifdef(symbol): | |
| 469 return symbol in defs | |
| 470 def pp_if(symbol): | |
| 471 return defs.get(symbol, False) | |
| 472 variable_map = { | |
| 473 'defs' : defs, | |
| 474 'os': target_platform, | |
| 475 | 464 |
| 476 # One of these is_xyz assertions gets set to True in the line | 465 elif name == 'is_linux': |
| 477 # following this initializer block. | 466 value = target_platform.startswith('linux') |
| 478 'is_linux': False, | 467 elif name == 'is_macosx': |
| 479 'is_macosx': False, | 468 value = target_platform == 'darwin' |
| 480 'is_win': False, | 469 elif name == 'is_win': |
| 481 'is_android': False, | 470 value = target_platform in ('cygwin', 'win32') |
| 482 'is_ios': False, | 471 elif name == 'is_android': |
| 472 value = target_platform == 'android' |
| 473 elif name == 'is_ios': |
| 474 value = target_platform == 'ios' |
| 475 elif name == 'is_posix': |
| 476 value = (target_platform in ('darwin', 'linux2', 'linux3', 'sunos5', |
| 477 'android', 'ios') |
| 478 or 'bsd' in target_platform) |
| 483 | 479 |
| 484 # is_posix is not mutually exclusive of the others and gets | 480 elif name == 'pp_ifdef': |
| 485 # set here, not below. | 481 def pp_ifdef(symbol): |
| 486 'is_posix': (target_platform in ('darwin', 'linux2', 'linux3', 'sunos5', | 482 return symbol in defs |
| 487 'android', 'ios') | 483 value = pp_ifdef |
| 488 or 'bsd' in target_platform), | 484 elif name == 'pp_if': |
| 485 def pp_if(symbol): |
| 486 return defs.get(symbol, False) |
| 487 value = pp_if |
| 489 | 488 |
| 490 'pp_ifdef' : pp_ifdef, | 489 elif name in defs: |
| 491 'pp_if' : pp_if, | 490 value = defs[name] |
| 492 } | 491 elif name in extra_variables: |
| 493 variable_map[Node.GetPlatformAssertion(target_platform)] = True | 492 value = extra_variables[name] |
| 493 else: |
| 494 # Undefined variables default to False. |
| 495 value = False |
| 494 | 496 |
| 495 if extra_variables: | 497 variable_map[name] = value |
| 496 variable_map.update(extra_variables) | 498 |
| 497 eval_result = cache_dict[expr] = eval(expr, {}, variable_map) | 499 eval_result = eval(code, {}, variable_map) |
| 500 assert isinstance(eval_result, bool) |
| 498 return eval_result | 501 return eval_result |
| 499 | 502 |
| 500 def EvaluateCondition(self, expr): | 503 def EvaluateCondition(self, expr): |
| 501 '''Returns true if and only if the Python expression 'expr' evaluates | 504 '''Returns true if and only if the Python expression 'expr' evaluates |
| 502 to true. | 505 to true. |
| 503 | 506 |
| 504 The expression is given a few local variables: | 507 The expression is given a few local variables: |
| 505 - 'lang' is the language currently being output | 508 - 'lang' is the language currently being output |
| 506 (the 'lang' attribute of the <output> element). | 509 (the 'lang' attribute of the <output> element). |
| 507 - 'context' is the current output context | 510 - 'context' is the current output context |
| 508 (the 'context' attribute of the <output> element). | 511 (the 'context' attribute of the <output> element). |
| 509 - 'defs' is a map of C preprocessor-style symbol names to their values. | 512 - 'defs' is a map of C preprocessor-style symbol names to their values. |
| 510 - 'os' is the current platform (likely 'linux2', 'win32' or 'darwin'). | 513 - 'os' is the current platform (likely 'linux2', 'win32' or 'darwin'). |
| 511 - 'pp_ifdef(symbol)' is a shorthand for "symbol in defs". | 514 - 'pp_ifdef(symbol)' is a shorthand for "symbol in defs". |
| 512 - 'pp_if(symbol)' is a shorthand for "symbol in defs and defs[symbol]". | 515 - 'pp_if(symbol)' is a shorthand for "symbol in defs and defs[symbol]". |
| 513 - 'is_linux', 'is_macosx', 'is_win', 'is_posix' are true if 'os' | 516 - 'is_linux', 'is_macosx', 'is_win', 'is_posix' are true if 'os' |
| 514 matches the given platform. | 517 matches the given platform. |
| 515 ''' | 518 ''' |
| 516 root = self.GetRoot() | 519 root = self.GetRoot() |
| 517 lang = getattr(root, 'output_language', '') | 520 lang = getattr(root, 'output_language', '') |
| 518 context = getattr(root, 'output_context', '') | 521 context = getattr(root, 'output_context', '') |
| 519 defs = getattr(root, 'defines', {}) | 522 defs = getattr(root, 'defines', {}) |
| 520 target_platform = getattr(root, 'target_platform', '') | 523 target_platform = getattr(root, 'target_platform', '') |
| 521 extra_variables = ( | 524 extra_variables = { |
| 522 ('lang', lang), | 525 'lang': lang, |
| 523 ('context', context), | 526 'context': context, |
| 524 ) | 527 } |
| 525 return Node.EvaluateExpression( | 528 return Node.EvaluateExpression( |
| 526 expr, defs, target_platform, extra_variables) | 529 expr, defs, target_platform, extra_variables) |
| 527 | 530 |
| 528 def OnlyTheseTranslations(self, languages): | 531 def OnlyTheseTranslations(self, languages): |
| 529 '''Turns off loading of translations for languages not in the provided list. | 532 '''Turns off loading of translations for languages not in the provided list. |
| 530 | 533 |
| 531 Attrs: | 534 Attrs: |
| 532 languages: ['fr', 'zh_cn'] | 535 languages: ['fr', 'zh_cn'] |
| 533 ''' | 536 ''' |
| 534 for node in self: | 537 for node in self: |
| (...skipping 49 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 584 def ExpandVariables(self): | 587 def ExpandVariables(self): |
| 585 '''Whether we need to expand variables on a given node.''' | 588 '''Whether we need to expand variables on a given node.''' |
| 586 return False | 589 return False |
| 587 | 590 |
| 588 | 591 |
| 589 class ContentNode(Node): | 592 class ContentNode(Node): |
| 590 '''Convenience baseclass for nodes that can have content.''' | 593 '''Convenience baseclass for nodes that can have content.''' |
| 591 def _ContentType(self): | 594 def _ContentType(self): |
| 592 return self._CONTENT_TYPE_MIXED | 595 return self._CONTENT_TYPE_MIXED |
| 593 | 596 |
| OLD | NEW |