From 86ac787889bef4051f3c936c087eb8a3f52eb157 Mon Sep 17 00:00:00 2001 From: Cheng Zhao Date: Fri, 12 Jan 2024 08:46:57 +0900 Subject: [PATCH] build: do not rely on gn_helpers in GN build MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit PR-URL: https://github.com/nodejs/node/pull/51439 Reviewed-By: Yagiz Nizipli Reviewed-By: Michaƫl Zasso Reviewed-By: Luigi Pinca --- deps/ngtcp2/unofficial.gni | 2 +- tools/gypi_to_gn.py | 128 +++++++++++++++++++++++++++++++------ 2 files changed, 110 insertions(+), 20 deletions(-) diff --git a/deps/ngtcp2/unofficial.gni b/deps/ngtcp2/unofficial.gni index 2c5890d71fe60a..26b8070c5a9c7f 100644 --- a/deps/ngtcp2/unofficial.gni +++ b/deps/ngtcp2/unofficial.gni @@ -2,7 +2,7 @@ # building official binaries. # Please edit the gyp files if you are making changes to build system. -import("//node/node.gni") +import("../../node.gni") # The actual configurations are put inside a template in unofficial.gni to # prevent accidental edits from contributors. diff --git a/tools/gypi_to_gn.py b/tools/gypi_to_gn.py index 77a68ff07ed4eb..08eb948f5b2a65 100755 --- a/tools/gypi_to_gn.py +++ b/tools/gypi_to_gn.py @@ -102,29 +102,119 @@ from __future__ import absolute_import from __future__ import print_function from optparse import OptionParser -import os import sys -# Look for standalone GN distribution. -def FindGNPath(): - for i in os.environ['PATH'].split(os.pathsep): - if i.rstrip(os.sep).endswith('gn'): - return i - return None +# This function is copied from build/gn_helpers.py in Chromium. +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|. -try: - # May already be in the import path. - import gn_helpers -except ImportError: - # Add src/build to import path. - src_dir = os.path.abspath(os.path.join(os.path.dirname(__file__), - os.pardir, os.pardir)) - sys.path.append(os.path.join(src_dir, 'build')) - if FindGNPath(): - sys.path.append(os.path.join(FindGNPath(), 'build')) - import gn_helpers + Raises: + ValueError: |value| cannot be printed to GN. + """ + + # Emits all output tokens without intervening whitespaces. + def GenerateTokens(v, level): + if isinstance(v, str): + 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, str): + raise ValueError('Dictionary key is not a string.') + if not key or key[0].isdigit() or not key.replace('_', '').isalnum(): + raise ValueError('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 ValueError('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 + + +def TranslateToGnChars(s): + for code in s.encode('utf-8'): + if code in (34, 36, 92): # For '"', '$', or '\\'. + yield '\\' + chr(code) + elif 32 <= code < 127: + yield chr(code) + else: + yield '$0x%02X' % code def LoadPythonDictionary(path): @@ -234,7 +324,7 @@ def main(): else: gn_dict[gn_key] = data[key] - print(gn_helpers.ToGNString(DeduplicateLists(gn_dict))) + print(ToGNString(DeduplicateLists(gn_dict))) if __name__ == '__main__': try: