1# Copyright 2020 The Chromium Authors. All rights reserved. 2# Use of this source code is governed by a BSD-style license that can be 3# found in the LICENSE file. 4 5import argparse 6import os.path 7import subprocess 8import sys 9 10import wayland_protocol_c_arg_handling 11import wayland_protocol_construction 12import wayland_protocol_data_classes 13import wayland_protocol_externals 14import wayland_protocol_identifiers 15 16 17def expand_template(template, context): 18 # type: (str, Mapping[str, Any]) -> str 19 """Expands the template using context, and returns the result""" 20 21 # Loaded from third_party/jinja2 after a sys.path modification 22 import jinja2 23 24 env = jinja2.Environment( 25 loader=jinja2.FileSystemLoader(os.path.dirname(template)), 26 keep_trailing_newline=True, # newline-terminate generated files 27 lstrip_blocks=True, 28 trim_blocks=True) # so don't need {%- -%} everywhere 29 30 # Additional global functions 31 env.globals['get_c_arg_for_server_request_arg'] = ( 32 wayland_protocol_c_arg_handling.get_c_arg_for_server_request_arg) 33 env.globals['get_c_arg_for_server_event_arg'] = ( 34 wayland_protocol_c_arg_handling.get_c_arg_for_server_event_arg) 35 env.globals['get_c_return_type_for_client_request'] = ( 36 wayland_protocol_c_arg_handling.get_c_return_type_for_client_request) 37 env.globals['get_c_arg_for_client_request_arg'] = ( 38 wayland_protocol_c_arg_handling.get_c_arg_for_client_request_arg) 39 env.globals['get_c_arg_for_client_event_arg'] = ( 40 wayland_protocol_c_arg_handling.get_c_arg_for_client_event_arg) 41 env.globals['get_construction_steps'] = ( 42 wayland_protocol_construction.get_construction_steps) 43 env.globals['get_destructor'] = ( 44 wayland_protocol_construction.get_destructor) 45 env.globals['get_external_interfaces_for_protocol'] = ( 46 wayland_protocol_externals.get_external_interfaces_for_protocol) 47 env.globals['get_minimum_version_to_construct'] = ( 48 wayland_protocol_construction.get_minimum_version_to_construct) 49 env.globals['get_versions_to_test_for_event_delivery'] = ( 50 wayland_protocol_construction.get_versions_to_test_for_event_delivery) 51 env.globals['is_global_interface'] = ( 52 wayland_protocol_construction.is_global_interface) 53 54 # Additional filters for transforming text 55 env.filters['kebab'] = wayland_protocol_identifiers.kebab_case 56 env.filters['pascal'] = wayland_protocol_identifiers.pascal_case 57 58 return env.get_template(os.path.basename(template)).render(context) 59 60 61def clang_format_source_text(source_text, clang_format_path, 62 effective_filename): 63 # type: (str, str, str) -> str 64 """Runs clang-format on source_text and returns the result.""" 65 # clang-format the output, for better readability and for 66 # -Wmisleading-indentation. 67 proc = subprocess.Popen( 68 [clang_format_path, '--assume-filename', effective_filename], 69 stdin=subprocess.PIPE, 70 stdout=subprocess.PIPE) 71 stdout_output, stderr_output = proc.communicate(input=source_text) 72 retcode = proc.wait() 73 if retcode != 0: 74 raise CalledProcessError(retcode, 75 'clang-format error: ' + stderr_output) 76 return stdout_output 77 78 79def write_if_changed(source_text, output): 80 # type: (str, str) -> None 81 """Writes source_text to output, but only if different.""" 82 if os.path.exists(output): 83 with open(output, 'rt') as infile: 84 if infile.read() == source_text: 85 return 86 87 with open(output, 'wt') as outfile: 88 outfile.write(source_text) 89 90 91def main(): 92 parser = argparse.ArgumentParser() 93 parser.add_argument('third_party_path') 94 parser.add_argument('clang_format_path') 95 parser.add_argument('template') 96 parser.add_argument('output') 97 parser.add_argument('protocols', nargs='+') 98 args = parser.parse_args() 99 100 # Allows us to import jinga2 101 sys.path.append(args.third_party_path) 102 103 protocols = wayland_protocol_data_classes.Protocols.parse_xml_files( 104 args.protocols) 105 106 if len(protocols.protocols) == 1: 107 expanded = expand_template(args.template, 108 {'protocol': protocols.protocols[0]}) 109 else: 110 expanded = expand_template(args.template, 111 {'protocols': protocols.protocols}) 112 113 formatted = clang_format_source_text(expanded, args.clang_format_path, 114 args.output) 115 write_if_changed(formatted, args.output) 116 117 118if __name__ == '__main__': 119 main() 120