| #!/usr/bin/python |
| # Copyright 2019 The ANGLE Project Authors. All rights reserved. |
| # Use of this source code is governed by a BSD-style license that can be |
| # found in the LICENSE file. |
| # |
| # generate_parser_tools.py: |
| # Common functionality to call flex and bison to generate lexer and parser of |
| # the translator and preprocessor. |
| |
| import os |
| import platform |
| import subprocess |
| import sys |
| |
| is_linux = platform.system() == 'Linux' |
| is_windows = platform.system() == 'Windows' |
| |
| |
| def get_tool_path_platform(tool_name, platform): |
| exe_path = os.path.join(sys.path[0], '..', '..', '..', 'tools', 'flex-bison', platform) |
| |
| return os.path.join(exe_path, tool_name) |
| |
| |
| def get_tool_path(tool_name): |
| if is_linux: |
| platform = 'linux' |
| ext = '' |
| else: |
| assert (is_windows) |
| platform = 'windows' |
| ext = '.exe' |
| |
| return get_tool_path_platform(tool_name + ext, platform) |
| |
| |
| def get_tool_file_sha1s(): |
| files = [ |
| get_tool_path_platform('flex', 'linux'), |
| get_tool_path_platform('bison', 'linux'), |
| get_tool_path_platform('flex.exe', 'windows'), |
| get_tool_path_platform('bison.exe', 'windows'), |
| get_tool_path_platform('m4.exe', 'windows') |
| ] |
| |
| files += [ |
| get_tool_path_platform(dll, 'windows') |
| for dll in ['msys-2.0.dll', 'msys-iconv-2.dll', 'msys-intl-8.dll'] |
| ] |
| |
| return [f + '.sha1' for f in files] |
| |
| |
| def run_flex(basename): |
| flex = get_tool_path('flex') |
| input_file = basename + '.l' |
| output_source = basename + '_lex_autogen.cpp' |
| |
| flex_args = [flex, '--noline', '--nounistd', '--outfile=' + output_source, input_file] |
| |
| flex_env = os.environ.copy() |
| if is_windows: |
| flex_env['M4'] = get_tool_path_platform('m4.exe', 'windows') |
| |
| process = subprocess.Popen(flex_args, env=flex_env, cwd=sys.path[0]) |
| process.communicate() |
| if process.returncode != 0: |
| return process.returncode |
| |
| # Patch flex output for 64-bit. The patch is simple enough that we could do a string |
| # replacement. More importantly, the location of the line of code that needs to be substituted |
| # can vary based on flex version, and the string substitution will find the correct place |
| # automatically. |
| |
| patch_in = """\n\t\tYY_INPUT( (&YY_CURRENT_BUFFER_LVALUE->yy_ch_buf[number_to_move]),\n\t\t\tyyg->yy_n_chars, num_to_read );""" |
| patch_out = """ |
| yy_size_t ret = 0; |
| YY_INPUT( (&YY_CURRENT_BUFFER_LVALUE->yy_ch_buf[number_to_move]), |
| ret, num_to_read ); |
| yyg->yy_n_chars = static_cast<int>(ret);""" |
| |
| with open(output_source, 'r') as flex_output: |
| output = flex_output.read() |
| |
| # If flex's output changes such that this line no longer exists, the patch needs to be |
| # updated, or possibly removed. |
| assert (output.find(patch_in) != -1) |
| |
| patched = output.replace(patch_in, patch_out) |
| |
| # Remove all tab characters from output. WebKit does not allow any tab characters in source |
| # files. |
| patched = patched.replace('\t', ' ') |
| |
| with open(output_source, 'w') as flex_output_patched: |
| flex_output_patched.write(patched) |
| |
| return 0 |
| |
| |
| def run_bison(basename, generate_header): |
| bison = get_tool_path('bison') |
| input_file = basename + '.y' |
| output_header = basename + '_tab_autogen.h' |
| output_source = basename + '_tab_autogen.cpp' |
| |
| bison_args = [bison, '--no-lines', '--skeleton=yacc.c'] |
| if generate_header: |
| bison_args += ['--defines=' + output_header] |
| bison_args += ['--output=' + output_source, input_file] |
| |
| bison_env = os.environ.copy() |
| bison_env['BISON_PKGDATADIR'] = get_tool_path_platform('', 'third_party') |
| if is_windows: |
| bison_env['M4'] = get_tool_path_platform('m4.exe', 'windows') |
| |
| process = subprocess.Popen(bison_args, env=bison_env, cwd=sys.path[0]) |
| process.communicate() |
| return process.returncode |
| |
| |
| def get_input_files(basename): |
| files = [basename + '.l', basename + '.y'] |
| return [os.path.join(sys.path[0], f) for f in files] |
| |
| |
| def get_output_files(basename, generate_header): |
| optional_header = [basename + '_tab_autogen.h'] if generate_header else [] |
| files = [basename + '_lex_autogen.cpp', basename + '_tab_autogen.cpp'] + optional_header |
| return [os.path.join(sys.path[0], f) for f in files] |
| |
| |
| def generate_parser(basename, generate_header): |
| # Handle inputs/outputs for run_code_generation.py's auto_script |
| if len(sys.argv) > 1: |
| if sys.argv[1] == 'inputs': |
| inputs = get_tool_file_sha1s() |
| inputs += get_input_files(basename) |
| current_file = __file__ |
| if current_file.endswith('.pyc'): |
| current_file = current_file[:-1] |
| inputs += [current_file] |
| print(','.join(inputs)) |
| if sys.argv[1] == 'outputs': |
| print(','.join(get_output_files(basename, generate_header))) |
| return 0 |
| |
| # Call flex and bison to generate the lexer and parser. |
| flex_result = run_flex(basename) |
| if flex_result != 0: |
| print 'Failed to run flex. Error ' + str(flex_result) |
| return 1 |
| |
| bison_result = run_bison(basename, generate_header) |
| if bison_result != 0: |
| print 'Failed to run bison. Error ' + str(bison_result) |
| return 2 |
| |
| return 0 |