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