From 300678066c63df763a632943fac3eed0512684b3 Mon Sep 17 00:00:00 2001 From: Chris Bracken Date: Fri, 30 Jul 2021 15:08:02 -0700 Subject: [PATCH] Update gn_helpers.py from upstream (#493) Updates our ToGNString implementation to match the Chromium version at version 4e21091c52ffc65be4df7839cc2e86575237e968 of the upstream repo https://chromium.googlesource.com/chromium/src/+/refs/heads/main/build/gn_helpers.py Also switches to absolute (buildroot relative) paths for invocations of vs_toolchain.py. --- build/config/win/visual_studio_version.gni | 2 +- build/gn_helpers.py | 142 +++++++++++++++++---- build/toolchain/win/BUILD.gn | 2 +- 3 files changed, 120 insertions(+), 26 deletions(-) diff --git a/build/config/win/visual_studio_version.gni b/build/config/win/visual_studio_version.gni index 782d8e8d88775..fbb55b74dd597 100644 --- a/build/config/win/visual_studio_version.gni +++ b/build/config/win/visual_studio_version.gni @@ -24,7 +24,7 @@ declare_args() { if (visual_studio_path == "") { _toolchain_data = - exec_script("../../vs_toolchain.py", [ "get_toolchain_dir" ], "scope") + exec_script("//build/vs_toolchain.py", [ "get_toolchain_dir" ], "scope") visual_studio_path = _toolchain_data.vs_path windows_sdk_path = _toolchain_data.sdk_path visual_studio_version = _toolchain_data.vs_version diff --git a/build/gn_helpers.py b/build/gn_helpers.py index 86b900cc45ac7..5878b6bbfed08 100644 --- a/build/gn_helpers.py +++ b/build/gn_helpers.py @@ -2,38 +2,132 @@ # 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 GNException(Exception): +class GNError(Exception): pass -def ToGNString(value, allow_dicts = True): - """Prints the given value to stdout. +# 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.') - allow_dicts indicates if this function will allow converting dictionaries - to GN scopes. This is only possible at the top level, you can't nest a - GN scope in a list, so this should be set to False for recursive calls.""" - if isinstance(value, str) or isinstance(value, str): - if value.find('\n') >= 0: - raise GNException("Trying to print a string with a newline in it.") - return '"' + value.replace('"', '\\"') + '"' + can_start = lambda tok: tok and tok not in ',}]=' + can_end = lambda tok: tok and tok not in ',{[=' - if isinstance(value, list): - return '[ %s ]' % ', '.join(ToGNString(v) for v in value) + # 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 - if isinstance(value, dict): - if not allow_dicts: - raise GNException("Attempting to recursively print a dictionary.") - result = "" - for key in value: - if not isinstance(key, str): - raise GNException("Dictionary key is not a string.") - result += "%s = %s\n" % (key, ToGNString(value[key], False)) - return result + # 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 - if isinstance(value, int): - return str(value) + 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 - raise GNException("Unsupported type %s (value %s) when printing to GN." % (type(value), value)) diff --git a/build/toolchain/win/BUILD.gn b/build/toolchain/win/BUILD.gn index f462af141fb54..81426c29bb562 100644 --- a/build/toolchain/win/BUILD.gn +++ b/build/toolchain/win/BUILD.gn @@ -56,7 +56,7 @@ if (current_toolchain == default_toolchain) { } else { configuration = "Release" } - exec_script("../../vs_toolchain.py", + exec_script("//build/vs_toolchain.py", [ "copy_dlls", rebase_path(root_build_dir),