• 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
10import os
11import platform
12import subprocess
13import sys
14
15is_linux = platform.system() == 'Linux'
16is_windows = platform.system() == 'Windows'
17
18
19def 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
25def 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
37def 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
54def 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
101def 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
122def 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
127def 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
133def 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