| # Copyright 2014 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. | |
| import sys | |
| """Helper functions useful when writing scripts that are run from GN's | |
| exec_script function.""" | |
| class GNError(Exception): | |
| pass | |
| # Computes ASCII code of an element of encoded Python 2 str / Python 3 bytes. | |
| _Ord = ord if sys.version_info.major < 3 else lambda c: c | |
| def _TranslateToGnChars(s): | |
| for decoded_ch in s.encode('utf-8'): # str in Python 2, bytes in Python 3. | |
| code = _Ord(decoded_ch) # int | |
| if code in (34, 36, 92): # For '"', '$', or '\\'. | |
| yield '\\' + chr(code) | |
| elif 32 <= code < 127: | |
| yield chr(code) | |
| else: | |
| yield '$0x%02X' % code | |
| def ToGNString(value, pretty=False): | |
| """Returns a stringified GN equivalent of a Python value. | |
| Args: | |
| value: The Python value to convert. | |
| pretty: Whether to pretty print. If true, then non-empty lists are rendered | |
| recursively with one item per line, with indents. Otherwise lists are | |
| rendered without new line. | |
| Returns: | |
| The stringified GN equivalent to |value|. | |
| Raises: | |
| GNError: |value| cannot be printed to GN. | |
| """ | |
| if sys.version_info.major < 3: | |
| basestring_compat = basestring | |
| else: | |
| basestring_compat = str | |
| # Emits all output tokens without intervening whitespaces. | |
| def GenerateTokens(v, level): | |
| if isinstance(v, basestring_compat): | |
| yield '"' + ''.join(_TranslateToGnChars(v)) + '"' | |
| elif isinstance(v, bool): | |
| yield 'true' if v else 'false' | |
| elif isinstance(v, int): | |
| yield str(v) | |
| elif isinstance(v, list): | |
| yield '[' | |
| for i, item in enumerate(v): | |
| if i > 0: | |
| yield ',' | |
| for tok in GenerateTokens(item, level + 1): | |
| yield tok | |
| yield ']' | |
| elif isinstance(v, dict): | |
| if level > 0: | |
| yield '{' | |
| for key in sorted(v): | |
| if not isinstance(key, basestring_compat): | |
| raise GNError('Dictionary key is not a string.') | |
| if not key or key[0].isdigit() or not key.replace('_', '').isalnum(): | |
| raise GNError('Dictionary key is not a valid GN identifier.') | |
| yield key # No quotations. | |
| yield '=' | |
| for tok in GenerateTokens(v[key], level + 1): | |
| yield tok | |
| if level > 0: | |
| yield '}' | |
| else: # Not supporting float: Add only when needed. | |
| raise GNError('Unsupported type when printing to GN.') | |
| can_start = lambda tok: tok and tok not in ',}]=' | |
| can_end = lambda tok: tok and tok not in ',{[=' | |
| # Adds whitespaces, trying to keep everything (except dicts) in 1 line. | |
| def PlainGlue(gen): | |
| prev_tok = None | |
| for i, tok in enumerate(gen): | |
| if i > 0: | |
| if can_end(prev_tok) and can_start(tok): | |
| yield '\n' # New dict item. | |
| elif prev_tok == '[' and tok == ']': | |
| yield ' ' # Special case for []. | |
| elif tok != ',': | |
| yield ' ' | |
| yield tok | |
| prev_tok = tok | |
| # Adds whitespaces so non-empty lists can span multiple lines, with indent. | |
| def PrettyGlue(gen): | |
| prev_tok = None | |
| level = 0 | |
| for i, tok in enumerate(gen): | |
| if i > 0: | |
| if can_end(prev_tok) and can_start(tok): | |
| yield '\n' + ' ' * level # New dict item. | |
| elif tok == '=' or prev_tok in '=': | |
| yield ' ' # Separator before and after '=', on same line. | |
| if tok in ']}': | |
| level -= 1 | |
| # Exclude '[]' and '{}' cases. | |
| if int(prev_tok == '[') + int(tok == ']') == 1 or \ | |
| int(prev_tok == '{') + int(tok == '}') == 1: | |
| yield '\n' + ' ' * level | |
| yield tok | |
| if tok in '[{': | |
| level += 1 | |
| if tok == ',': | |
| yield '\n' + ' ' * level | |
| prev_tok = tok | |
| token_gen = GenerateTokens(value, 0) | |
| ret = ''.join((PrettyGlue if pretty else PlainGlue)(token_gen)) | |
| # Add terminating '\n' for dict |value| or multi-line output. | |
| if isinstance(value, dict) or '\n' in ret: | |
| return ret + '\n' | |
| return ret | |