1# -*- Mode: Python -*- 2# coding=utf-8 3 4# GDBus - GLib D-Bus Library 5# 6# Copyright (C) 2008-2011 Red Hat, Inc. 7# Copyright (C) 2018 Iñigo Martínez <inigomartinez@gmail.com> 8# 9# This library is free software; you can redistribute it and/or 10# modify it under the terms of the GNU Lesser General Public 11# License as published by the Free Software Foundation; either 12# version 2.1 of the License, or (at your option) any later version. 13# 14# This library is distributed in the hope that it will be useful, 15# but WITHOUT ANY WARRANTY; without even the implied warranty of 16# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 17# Lesser General Public License for more details. 18# 19# You should have received a copy of the GNU Lesser General 20# Public License along with this library; if not, see <http://www.gnu.org/licenses/>. 21# 22# Author: David Zeuthen <davidz@redhat.com> 23 24import argparse 25import os 26import sys 27 28from . import config 29from . import dbustypes 30from . import parser 31from . import codegen 32from . import codegen_docbook 33from .utils import print_error, print_warning 34 35def find_arg(arg_list, arg_name): 36 for a in arg_list: 37 if a.name == arg_name: 38 return a 39 return None 40 41def find_method(iface, method): 42 for m in iface.methods: 43 if m.name == method: 44 return m 45 return None 46 47def find_signal(iface, signal): 48 for m in iface.signals: 49 if m.name == signal: 50 return m 51 return None 52 53def find_prop(iface, prop): 54 for m in iface.properties: 55 if m.name == prop: 56 return m 57 return None 58 59def apply_annotation(iface_list, iface, method, signal, prop, arg, key, value): 60 iface_obj = None 61 for i in iface_list: 62 if i.name == iface: 63 iface_obj = i 64 break 65 66 if iface_obj is None: 67 print_error('No interface "{}"'.format(iface)) 68 69 target_obj = None 70 71 if method: 72 method_obj = find_method(iface_obj, method) 73 if method_obj is None: 74 print_error('No method "{}" on interface "{}"'.format(method, iface)) 75 if arg: 76 arg_obj = find_arg(method_obj.in_args, arg) 77 if (arg_obj is None): 78 arg_obj = find_arg(method_obj.out_args, arg) 79 if (arg_obj is None): 80 print_error('No arg "{}" on method "{}" on interface "{}"'.format(arg, method, iface)) 81 target_obj = arg_obj 82 else: 83 target_obj = method_obj 84 elif signal: 85 signal_obj = find_signal(iface_obj, signal) 86 if signal_obj is None: 87 print_error('No signal "{}" on interface "{}"'.format(signal, iface)) 88 if arg: 89 arg_obj = find_arg(signal_obj.args, arg) 90 if (arg_obj is None): 91 print_error('No arg "{}" on signal "{}" on interface "{}"'.format(arg, signal, iface)) 92 target_obj = arg_obj 93 else: 94 target_obj = signal_obj 95 elif prop: 96 prop_obj = find_prop(iface_obj, prop) 97 if prop_obj is None: 98 print_error('No property "{}" on interface "{}"'.format(prop, iface)) 99 target_obj = prop_obj 100 else: 101 target_obj = iface_obj 102 target_obj.annotations.insert(0, dbustypes.Annotation(key, value)) 103 104 105def apply_annotations(iface_list, annotation_list): 106 # apply annotations given on the command line 107 for (what, key, value) in annotation_list: 108 pos = what.find('::') 109 if pos != -1: 110 # signal 111 iface = what[0:pos]; 112 signal = what[pos + 2:] 113 pos = signal.find('[') 114 if pos != -1: 115 arg = signal[pos + 1:] 116 signal = signal[0:pos] 117 pos = arg.find(']') 118 arg = arg[0:pos] 119 apply_annotation(iface_list, iface, None, signal, None, arg, key, value) 120 else: 121 apply_annotation(iface_list, iface, None, signal, None, None, key, value) 122 else: 123 pos = what.find(':') 124 if pos != -1: 125 # property 126 iface = what[0:pos]; 127 prop = what[pos + 1:] 128 apply_annotation(iface_list, iface, None, None, prop, None, key, value) 129 else: 130 pos = what.find('()') 131 if pos != -1: 132 # method 133 combined = what[0:pos] 134 pos = combined.rfind('.') 135 iface = combined[0:pos] 136 method = combined[pos + 1:] 137 pos = what.find('[') 138 if pos != -1: 139 arg = what[pos + 1:] 140 pos = arg.find(']') 141 arg = arg[0:pos] 142 apply_annotation(iface_list, iface, method, None, None, arg, key, value) 143 else: 144 apply_annotation(iface_list, iface, method, None, None, None, key, value) 145 else: 146 # must be an interface 147 iface = what 148 apply_annotation(iface_list, iface, None, None, None, None, key, value) 149 150def codegen_main(): 151 arg_parser = argparse.ArgumentParser(description='D-Bus code and documentation generator') 152 arg_parser.add_argument('files', metavar='FILE', nargs='*', 153 help='D-Bus introspection XML file') 154 arg_parser.add_argument('--xml-files', metavar='FILE', action='append', default=[], 155 help=argparse.SUPPRESS) 156 arg_parser.add_argument('--interface-prefix', metavar='PREFIX', default='', 157 help='String to strip from D-Bus interface names for code and docs') 158 arg_parser.add_argument('--c-namespace', metavar='NAMESPACE', default='', 159 help='The namespace to use for generated C code') 160 arg_parser.add_argument('--c-generate-object-manager', action='store_true', 161 help='Generate a GDBusObjectManagerClient subclass when generating C code') 162 arg_parser.add_argument('--c-generate-autocleanup', choices=['none', 'objects', 'all'], default='objects', 163 help='Generate autocleanup support') 164 arg_parser.add_argument('--generate-docbook', metavar='OUTFILES', 165 help='Generate Docbook in OUTFILES-org.Project.IFace.xml') 166 arg_parser.add_argument('--pragma-once', action='store_true', 167 help='Use "pragma once" as the inclusion guard') 168 arg_parser.add_argument('--annotate', nargs=3, action='append', metavar='WHAT KEY VALUE', 169 help='Add annotation (may be used several times)') 170 171 group = arg_parser.add_mutually_exclusive_group() 172 group.add_argument('--generate-c-code', metavar='OUTFILES', 173 help='Generate C code in OUTFILES.[ch]') 174 group.add_argument('--header', action='store_true', 175 help='Generate C headers') 176 group.add_argument('--body', action='store_true', 177 help='Generate C code') 178 group.add_argument('--interface-info-header', action='store_true', 179 help='Generate GDBusInterfaceInfo C header') 180 group.add_argument('--interface-info-body', action='store_true', 181 help='Generate GDBusInterfaceInfo C code') 182 183 group = arg_parser.add_mutually_exclusive_group() 184 group.add_argument('--output', metavar='FILE', 185 help='Write output into the specified file') 186 group.add_argument('--output-directory', metavar='OUTDIR', default='', 187 help='Location to output generated files') 188 189 args = arg_parser.parse_args(); 190 191 if len(args.xml_files) > 0: 192 print_warning('The "--xml-files" option is deprecated; use positional arguments instead') 193 194 if ((args.generate_c_code is not None or args.generate_docbook is not None) and 195 args.output is not None): 196 print_error('Using --generate-c-code or --generate-docbook and ' 197 '--output at the same time is not allowed') 198 199 if args.generate_c_code: 200 header_name = args.generate_c_code + '.h' 201 h_file = os.path.join(args.output_directory, header_name) 202 args.header = True 203 c_file = os.path.join(args.output_directory, args.generate_c_code + '.c') 204 args.body = True 205 elif args.header: 206 if args.output is None: 207 print_error('Using --header requires --output') 208 209 h_file = args.output 210 header_name = os.path.basename(h_file) 211 elif args.body: 212 if args.output is None: 213 print_error('Using --body requires --output') 214 215 c_file = args.output 216 header_name = os.path.splitext(os.path.basename(c_file))[0] + '.h' 217 elif args.interface_info_header: 218 if args.output is None: 219 print_error('Using --interface-info-header requires --output') 220 if args.c_generate_object_manager: 221 print_error('--c-generate-object-manager is incompatible with ' 222 '--interface-info-header') 223 224 h_file = args.output 225 header_name = os.path.basename(h_file) 226 elif args.interface_info_body: 227 if args.output is None: 228 print_error('Using --interface-info-body requires --output') 229 if args.c_generate_object_manager: 230 print_error('--c-generate-object-manager is incompatible with ' 231 '--interface-info-body') 232 233 c_file = args.output 234 header_name = os.path.splitext(os.path.basename(c_file))[0] + '.h' 235 236 all_ifaces = [] 237 input_files_basenames = [] 238 for fname in sorted(args.files + args.xml_files): 239 with open(fname, 'rb') as f: 240 xml_data = f.read() 241 parsed_ifaces = parser.parse_dbus_xml(xml_data) 242 all_ifaces.extend(parsed_ifaces) 243 input_files_basenames.append(os.path.basename(fname)) 244 245 if args.annotate is not None: 246 apply_annotations(all_ifaces, args.annotate) 247 248 for i in all_ifaces: 249 i.post_process(args.interface_prefix, args.c_namespace) 250 251 docbook = args.generate_docbook 252 docbook_gen = codegen_docbook.DocbookCodeGenerator(all_ifaces); 253 if docbook: 254 ret = docbook_gen.generate(docbook, args.output_directory) 255 256 if args.header: 257 with open(h_file, 'w') as outfile: 258 gen = codegen.HeaderCodeGenerator(all_ifaces, 259 args.c_namespace, 260 args.c_generate_object_manager, 261 args.c_generate_autocleanup, 262 header_name, 263 input_files_basenames, 264 args.pragma_once, 265 outfile) 266 gen.generate() 267 268 if args.body: 269 with open(c_file, 'w') as outfile: 270 gen = codegen.CodeGenerator(all_ifaces, 271 args.c_namespace, 272 args.c_generate_object_manager, 273 header_name, 274 input_files_basenames, 275 docbook_gen, 276 outfile) 277 gen.generate() 278 279 if args.interface_info_header: 280 with open(h_file, 'w') as outfile: 281 gen = codegen.InterfaceInfoHeaderCodeGenerator(all_ifaces, 282 args.c_namespace, 283 header_name, 284 input_files_basenames, 285 args.pragma_once, 286 outfile) 287 gen.generate() 288 289 if args.interface_info_body: 290 with open(c_file, 'w') as outfile: 291 gen = codegen.InterfaceInfoBodyCodeGenerator(all_ifaces, 292 args.c_namespace, 293 header_name, 294 input_files_basenames, 295 outfile) 296 gen.generate() 297 298 sys.exit(0) 299 300if __name__ == "__main__": 301 codegen_main() 302