| #!/usr/bin/python3 | |
| # 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 %s' % str(flex_result)) | |
| return 1 | |
| bison_result = run_bison(basename, generate_header) | |
| if bison_result != 0: | |
| print('Failed to run bison. Error %s' % str(bison_result)) | |
| return 2 | |
| return 0 |