From f427fd8d3425c84ebb5bc5f5747b248de627b302 Mon Sep 17 00:00:00 2001
From: serge-sans-paille <serge.guelton@telecom-bretagne.eu>
Date: Mon, 2 Nov 2020 13:59:31 +0100
Subject: [PATCH] Fix peg parser in presence of profile data

---
 Tools/peg_generator/pegen/testutil.py | 21 ++++++++++++++++++++-
 1 file changed, 20 insertions(+), 1 deletion(-)

diff --git a/Tools/peg_generator/pegen/testutil.py b/Tools/peg_generator/pegen/testutil.py
index 920d246..e898a9b 100644
--- a/Tools/peg_generator/pegen/testutil.py
+++ b/Tools/peg_generator/pegen/testutil.py
@@ -1,133 +1,152 @@
 import importlib.util
 import io
 import os
 import pathlib
 import sys
 import textwrap
 import tokenize
 import token
 
 from typing import Any, cast, Dict, IO, Type, Final
 
 from pegen.build import compile_c_extension
 from pegen.c_generator import CParserGenerator
 from pegen.grammar import Grammar
 from pegen.grammar_parser import GeneratedParser as GrammarParser
 from pegen.parser import Parser
 from pegen.python_generator import PythonParserGenerator
 from pegen.tokenizer import Tokenizer
 
 ALL_TOKENS = token.tok_name
 EXACT_TOKENS = token.EXACT_TOKEN_TYPES  # type: ignore
 NON_EXACT_TOKENS = {
     name for index, name in token.tok_name.items() if index not in EXACT_TOKENS.values()
 }
 
 
 def generate_parser(grammar: Grammar) -> Type[Parser]:
     # Generate a parser.
     out = io.StringIO()
     genr = PythonParserGenerator(grammar, out)
     genr.generate("<string>")
 
     # Load the generated parser class.
     ns: Dict[str, Any] = {}
     exec(out.getvalue(), ns)
     return ns["GeneratedParser"]
 
 
 def run_parser(file: IO[bytes], parser_class: Type[Parser], *, verbose: bool = False) -> Any:
     # Run a parser on a file (stream).
     tokenizer = Tokenizer(tokenize.generate_tokens(file.readline))  # type: ignore # typeshed issue #3515
     parser = parser_class(tokenizer, verbose=verbose)
     result = parser.start()
     if result is None:
         raise parser.make_syntax_error()
     return result
 
 
 def parse_string(
     source: str, parser_class: Type[Parser], *, dedent: bool = True, verbose: bool = False
 ) -> Any:
     # Run the parser on a string.
     if dedent:
         source = textwrap.dedent(source)
     file = io.StringIO(source)
     return run_parser(file, parser_class, verbose=verbose)  # type: ignore # typeshed issue #3515
 
 
 def make_parser(source: str) -> Type[Parser]:
     # Combine parse_string() and generate_parser().
     grammar = parse_string(source, GrammarParser)
     return generate_parser(grammar)
 
 
 def import_file(full_name: str, path: str) -> Any:
     """Import a python module from a path"""
 
     spec = importlib.util.spec_from_file_location(full_name, path)
     mod = importlib.util.module_from_spec(spec)
 
     # We assume this is not None and has an exec_module() method.
     # See https://docs.python.org/3/reference/import.html?highlight=exec_module#loading
     loader = cast(Any, spec.loader)
     loader.exec_module(mod)
     return mod
 
 
 def generate_c_parser_source(grammar: Grammar) -> str:
     out = io.StringIO()
     genr = CParserGenerator(grammar, ALL_TOKENS, EXACT_TOKENS, NON_EXACT_TOKENS, out)
     genr.generate("<string>")
     return out.getvalue()
 
 
 def generate_parser_c_extension(
     grammar: Grammar, path: pathlib.PurePath, debug: bool = False
 ) -> Any:
     """Generate a parser c extension for the given grammar in the given path
 
     Returns a module object with a parse_string() method.
     TODO: express that using a Protocol.
     """
     # Make sure that the working directory is empty: reusing non-empty temporary
     # directories when generating extensions can lead to segmentation faults.
     # Check issue #95 (https://github.com/gvanrossum/pegen/issues/95) for more
     # context.
     assert not os.listdir(path)
     source = path / "parse.c"
     with open(source, "w", encoding="utf-8") as file:
         genr = CParserGenerator(
             grammar, ALL_TOKENS, EXACT_TOKENS, NON_EXACT_TOKENS, file, debug=debug
         )
         genr.generate("parse.c")
-    compile_c_extension(str(source), build_dir=str(path))
+
+    # Monkey patch CFLAGS to remove profiling flags; These files are not
+    # especially relevant for testing, 
+    import pegen.build
+    get_extra_flags = pegen.build.get_extra_flags
+    def custom_get_extra_flags(compiler_flags, compiler_py_flags_nodist):
+        extra_flags = get_extra_flags(compiler_flags, compiler_py_flags_nodist)
+        if compiler_py_flags_nodist == 'PY_CFLAGS_NODIST':
+            import sysconfig
+            try:
+                extra_flags.remove(sysconfig.get_config_var('PGO_PROF_USE_FLAG'))
+            except ValueError:
+                pass
+        return extra_flags
+
+    pegen.build.get_extra_flags = custom_get_extra_flags
+    try:
+        compile_c_extension(str(source), build_dir=str(path))
+    finally:
+        pegen.build.get_extra_flags = get_extra_flags
 
 
 def print_memstats() -> bool:
     MiB: Final = 2 ** 20
     try:
         import psutil  # type: ignore
     except ImportError:
         return False
     print("Memory stats:")
     process = psutil.Process()
     meminfo = process.memory_info()
     res = {}
     res["rss"] = meminfo.rss / MiB
     res["vms"] = meminfo.vms / MiB
     if sys.platform == "win32":
         res["maxrss"] = meminfo.peak_wset / MiB
     else:
         # See https://stackoverflow.com/questions/938733/total-memory-used-by-python-process
         import resource  # Since it doesn't exist on Windows.
 
         rusage = resource.getrusage(resource.RUSAGE_SELF)
         if sys.platform == "darwin":
             factor = 1
         else:
             factor = 1024  # Linux
         res["maxrss"] = rusage.ru_maxrss * factor / MiB
     for key, value in res.items():
         print(f"  {key:12.12s}: {value:10.0f} MiB")
     return True
-- 
1.8.3.1

