xref: /aosp_15_r20/external/mesa3d/meson_to_hermetic/generate_python_build.py (revision 6104692788411f58d303aa86923a9ff6ecaded22)
1import getopt
2import os
3import re
4import sys
5from meson2python import meson2python
6from jinja2 import Environment, FileSystemLoader
7from pathlib import Path
8
9environment = Environment(
10    loader=FileSystemLoader(Path(__file__).parent.resolve() / 'templates/')
11)
12generator_template = environment.get_template('generate_python_build.txt')
13
14
15# Converts the given |file_name| from meson to python, and writes the python code
16# to the given |file|.  Code is indented by |output_indent|.  When a subdir command
17# is found, the meson.build build in that subdir is converted by recursively invoking
18# this function.
19def process_meson(file_name: str, output_indent: str = ''):
20    python_code = ''
21    python_code += (
22        output_indent
23        + '########################################################################################################################'
24    )
25    python_code += '\n' + output_indent + f'### Begin conversion from: {file_name}'
26    python_code += (
27        '\n'
28        + output_indent
29        + '########################################################################################################################'
30    )
31
32    print('Processing: ' + file_name)
33    sys.stdout.flush()
34
35    content = meson2python(file_name)
36    inside_literal = False
37
38    for line in content.splitlines():
39        # Remove line terminator
40        line = line.rstrip()
41
42        # Check for multiline literals.
43        # We ignore literals that start and end on one line, though that may cause
44        # problems for the line processing below.
45        matches = re.findall(r"'''", line)
46
47        literal_delimiter_count = len(matches)
48
49        line_prefix = ''
50        line_suffix = ''
51        if literal_delimiter_count == 1:
52            inside_literal = not inside_literal
53            literal_line_split = line.split(r"'''")
54            if inside_literal:
55                line = literal_line_split[0]
56                line_suffix = r"'''" + literal_line_split[1]
57            else:
58                line_prefix = literal_line_split[0] + r"'''"
59                line = literal_line_split[1]
60        elif literal_delimiter_count == 0 or literal_delimiter_count == 2:
61            if inside_literal:
62                # Don't match anything while inside literal
63                line_prefix = line
64                line = ''
65        else:
66            exit('Unhandled literal in line: ' + line)
67
68        # Recurse into subdirs
69        match = re.match("( *)subdir\('([a-zA-Z0-9_\-/]+)'\)", line)
70        if match is not None:
71            subdir_output_indent = match.group(1) + output_indent
72            current_dir = os.path.dirname(file_name)
73            next_dir = os.path.join(current_dir, match.group(2))
74            next_file = os.path.join(next_dir, 'meson.build')
75            # Ensure the build definitions are aware of the changing directory
76            python_code += f"\n{subdir_output_indent}set_relative_dir('{next_dir}')"
77            python_code += '\n' + process_meson(next_file, subdir_output_indent)
78            python_code += f"\n{subdir_output_indent}set_relative_dir('{current_dir}')"
79            continue
80
81        python_code += f'\n{output_indent + line_prefix + line + line_suffix}'
82    python_code += (
83        '\n'
84        + output_indent
85        + '########################################################################################################################'
86    )
87    python_code += '\n' + output_indent + f'### End conversion from: {file_name}'
88    python_code += (
89        '\n'
90        + output_indent
91        + '########################################################################################################################'
92    )
93    return python_code
94
95
96def generate(target: str):
97    if not (target == 'android' or target == 'fuchsia'):
98        exit('Target must be android or fuchsia')
99
100    output_file_name = 'generate_%s_build.py' % target
101    print('Writing to: ' + output_file_name)
102
103    meson_options = process_meson('meson_options.txt')
104    meson_build = process_meson('meson.build')
105    content = generator_template.render(
106        meson_options=meson_options,
107        meson_build=meson_build,
108    )
109    with open(output_file_name, 'w') as file:
110        file.write(content)
111
112
113def usage():
114    print('Usage: -t [android|fuchsia]')
115    sys.exit()
116
117
118def main(argv):
119    target = 'android'
120    try:
121        opts, args = getopt.getopt(
122            argv,
123            'ht:',
124            [
125                'help',
126                'target=',
127            ],
128        )
129        for opt, arg in opts:
130            if opt in ('-h', '--help'):
131                usage()
132            elif opt in ('-t', '--target'):
133                target = arg
134    except getopt.GetoptError as _:
135        usage()
136
137    generate(target)
138
139
140if __name__ == '__main__':
141    main(sys.argv[1:])
142