• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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