1 #!/usr/bin/python 2 # Copyright 2019 The ANGLE Project Authors. All rights reserved. 3 # Use of this source code is governed by a BSD-style license that can be 4 # found in the LICENSE file. 5 # 6 # generate_parser_tools.py: 7 # Common functionality to call flex and bison to generate lexer and parser of 8 # the translator and preprocessor. 9 10 import os 11 import platform 12 import subprocess 13 import sys 14 15 is_linux = platform.system() == 'Linux' 16 is_windows = platform.system() == 'Windows' 17 18 19 def get_tool_path_platform(tool_name, platform): 20 exe_path = os.path.join(sys.path[0], '..', '..', '..', 'tools', 'flex-bison', platform) 21 22 return os.path.join(exe_path, tool_name) 23 24 25 def get_tool_path(tool_name): 26 if is_linux: 27 platform = 'linux' 28 ext = '' 29 else: 30 assert (is_windows) 31 platform = 'windows' 32 ext = '.exe' 33 34 return get_tool_path_platform(tool_name + ext, platform) 35 36 37 def get_tool_file_sha1s(): 38 files = [ 39 get_tool_path_platform('flex', 'linux'), 40 get_tool_path_platform('bison', 'linux'), 41 get_tool_path_platform('flex.exe', 'windows'), 42 get_tool_path_platform('bison.exe', 'windows'), 43 get_tool_path_platform('m4.exe', 'windows') 44 ] 45 46 files += [ 47 get_tool_path_platform(dll, 'windows') 48 for dll in ['msys-2.0.dll', 'msys-iconv-2.dll', 'msys-intl-8.dll'] 49 ] 50 51 return [f + '.sha1' for f in files] 52 53 54 def run_flex(basename): 55 flex = get_tool_path('flex') 56 input_file = basename + '.l' 57 output_source = basename + '_lex_autogen.cpp' 58 59 flex_args = [flex, '--noline', '--nounistd', '--outfile=' + output_source, input_file] 60 61 flex_env = os.environ.copy() 62 if is_windows: 63 flex_env['M4'] = get_tool_path_platform('m4.exe', 'windows') 64 65 process = subprocess.Popen(flex_args, env=flex_env, cwd=sys.path[0]) 66 process.communicate() 67 if process.returncode != 0: 68 return process.returncode 69 70 # Patch flex output for 64-bit. The patch is simple enough that we could do a string 71 # replacement. More importantly, the location of the line of code that needs to be substituted 72 # can vary based on flex version, and the string substitution will find the correct place 73 # automatically. 74 75 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 );""" 76 patch_out = """ 77 yy_size_t ret = 0; 78 YY_INPUT( (&YY_CURRENT_BUFFER_LVALUE->yy_ch_buf[number_to_move]), 79 ret, num_to_read ); 80 yyg->yy_n_chars = static_cast<int>(ret);""" 81 82 with open(output_source, 'r') as flex_output: 83 output = flex_output.read() 84 85 # If flex's output changes such that this line no longer exists, the patch needs to be 86 # updated, or possibly removed. 87 assert (output.find(patch_in) != -1) 88 89 patched = output.replace(patch_in, patch_out) 90 91 # Remove all tab characters from output. WebKit does not allow any tab characters in source 92 # files. 93 patched = patched.replace('\t', ' ') 94 95 with open(output_source, 'w') as flex_output_patched: 96 flex_output_patched.write(patched) 97 98 return 0 99 100 101 def run_bison(basename, generate_header): 102 bison = get_tool_path('bison') 103 input_file = basename + '.y' 104 output_header = basename + '_tab_autogen.h' 105 output_source = basename + '_tab_autogen.cpp' 106 107 bison_args = [bison, '--no-lines', '--skeleton=yacc.c'] 108 if generate_header: 109 bison_args += ['--defines=' + output_header] 110 bison_args += ['--output=' + output_source, input_file] 111 112 bison_env = os.environ.copy() 113 bison_env['BISON_PKGDATADIR'] = get_tool_path_platform('', 'third_party') 114 if is_windows: 115 bison_env['M4'] = get_tool_path_platform('m4.exe', 'windows') 116 117 process = subprocess.Popen(bison_args, env=bison_env, cwd=sys.path[0]) 118 process.communicate() 119 return process.returncode 120 121 122 def get_input_files(basename): 123 files = [basename + '.l', basename + '.y'] 124 return [os.path.join(sys.path[0], f) for f in files] 125 126 127 def get_output_files(basename, generate_header): 128 optional_header = [basename + '_tab_autogen.h'] if generate_header else [] 129 files = [basename + '_lex_autogen.cpp', basename + '_tab_autogen.cpp'] + optional_header 130 return [os.path.join(sys.path[0], f) for f in files] 131 132 133 def generate_parser(basename, generate_header): 134 # Handle inputs/outputs for run_code_generation.py's auto_script 135 if len(sys.argv) > 1: 136 if sys.argv[1] == 'inputs': 137 inputs = get_tool_file_sha1s() 138 inputs += get_input_files(basename) 139 current_file = __file__ 140 if current_file.endswith('.pyc'): 141 current_file = current_file[:-1] 142 inputs += [current_file] 143 print(','.join(inputs)) 144 if sys.argv[1] == 'outputs': 145 print(','.join(get_output_files(basename, generate_header))) 146 return 0 147 148 # Call flex and bison to generate the lexer and parser. 149 flex_result = run_flex(basename) 150 if flex_result != 0: 151 print 'Failed to run flex. Error ' + str(flex_result) 152 return 1 153 154 bison_result = run_bison(basename, generate_header) 155 if bison_result != 0: 156 print 'Failed to run bison. Error ' + str(bison_result) 157 return 2 158 159 return 0 160