4 # GDBus - GLib D-Bus Library
6 # Copyright (C) 2008-2011 Red Hat, Inc.
7 # Copyright (C) 2018 Iñigo Martínez <inigomartinez@gmail.com>
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.
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.
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/>.
22 # Author: David Zeuthen <davidz@redhat.com>
29 from . import dbustypes
32 from . import codegen_docbook
33 from .utils import print_error, print_warning
36 def find_arg(arg_list, arg_name):
38 if a.name == arg_name:
43 def find_method(iface, method):
44 for m in iface.methods:
50 def find_signal(iface, signal):
51 for m in iface.signals:
57 def find_prop(iface, prop):
58 for m in iface.properties:
64 def apply_annotation(iface_list, iface, method, signal, prop, arg, key, value):
72 print_error('No interface "{}"'.format(iface))
77 method_obj = find_method(iface_obj, method)
78 if method_obj is None:
79 print_error('No method "{}" on interface "{}"'.format(method, iface))
81 arg_obj = find_arg(method_obj.in_args, arg)
83 arg_obj = find_arg(method_obj.out_args, arg)
86 'No arg "{}" on method "{}" on interface "{}"'.format(
92 target_obj = method_obj
94 signal_obj = find_signal(iface_obj, signal)
95 if signal_obj is None:
96 print_error('No signal "{}" on interface "{}"'.format(signal, iface))
98 arg_obj = find_arg(signal_obj.args, arg)
101 'No arg "{}" on signal "{}" on interface "{}"'.format(
107 target_obj = signal_obj
109 prop_obj = find_prop(iface_obj, prop)
111 print_error('No property "{}" on interface "{}"'.format(prop, iface))
112 target_obj = prop_obj
114 target_obj = iface_obj
115 target_obj.annotations.insert(0, dbustypes.Annotation(key, value))
118 def apply_annotations(iface_list, annotation_list):
119 # apply annotations given on the command line
120 for (what, key, value) in annotation_list:
121 pos = what.find("::")
125 signal = what[pos + 2 :]
126 pos = signal.find("[")
128 arg = signal[pos + 1 :]
129 signal = signal[0:pos]
132 apply_annotation(iface_list, iface, None, signal, None, arg, key, value)
135 iface_list, iface, None, signal, None, None, key, value
142 prop = what[pos + 1 :]
143 apply_annotation(iface_list, iface, None, None, prop, None, key, value)
145 pos = what.find("()")
148 combined = what[0:pos]
149 pos = combined.rfind(".")
150 iface = combined[0:pos]
151 method = combined[pos + 1 :]
154 arg = what[pos + 1 :]
158 iface_list, iface, method, None, None, arg, key, value
162 iface_list, iface, method, None, None, None, key, value
165 # must be an interface
168 iface_list, iface, None, None, None, None, key, value
173 arg_parser = argparse.ArgumentParser(
174 description="D-Bus code and documentation generator"
176 arg_parser.add_argument(
177 "files", metavar="FILE", nargs="+", help="D-Bus introspection XML file"
179 arg_parser.add_argument(
184 help=argparse.SUPPRESS,
186 arg_parser.add_argument(
187 "--interface-prefix",
190 help="String to strip from D-Bus interface names for code and docs",
192 arg_parser.add_argument(
196 help="The namespace to use for generated C code",
198 arg_parser.add_argument(
199 "--c-generate-object-manager",
201 help="Generate a GDBusObjectManagerClient subclass when generating C code",
203 arg_parser.add_argument(
204 "--c-generate-autocleanup",
205 choices=["none", "objects", "all"],
207 help="Generate autocleanup support",
209 arg_parser.add_argument(
210 "--generate-docbook",
212 help="Generate Docbook in OUTFILES-org.Project.IFace.xml",
214 arg_parser.add_argument(
217 help='Use "pragma once" as the inclusion guard',
219 arg_parser.add_argument(
223 metavar="WHAT KEY VALUE",
224 help="Add annotation (may be used several times)",
226 arg_parser.add_argument(
227 "--glib-min-required",
229 help="Minimum version of GLib to be supported by the outputted code "
232 arg_parser.add_argument(
233 "--glib-max-allowed",
235 help="Maximum version of GLib to be used by the outputted code "
236 "(default: current GLib version)",
238 arg_parser.add_argument(
239 "--symbol-decorator",
240 help="Macro used to decorate a symbol in the outputted header, "
241 "possibly to export symbols",
243 arg_parser.add_argument(
244 "--symbol-decorator-header",
245 help="Additional header required for decorator specified by "
246 "--symbol-decorator",
248 arg_parser.add_argument(
249 "--symbol-decorator-define",
250 help="Additional define required for decorator specified by "
251 "--symbol-decorator",
254 group = arg_parser.add_mutually_exclusive_group()
256 "--generate-c-code", metavar="OUTFILES", help="Generate C code in OUTFILES.[ch]"
258 group.add_argument("--header", action="store_true", help="Generate C headers")
259 group.add_argument("--body", action="store_true", help="Generate C code")
261 "--interface-info-header",
263 help="Generate GDBusInterfaceInfo C header",
266 "--interface-info-body",
268 help="Generate GDBusInterfaceInfo C code",
271 group = arg_parser.add_mutually_exclusive_group()
273 "--output", metavar="FILE", help="Write output into the specified file"
276 "--output-directory",
279 help="Location to output generated files",
282 args = arg_parser.parse_args()
284 if len(args.xml_files) > 0:
286 'The "--xml-files" option is deprecated; use positional arguments instead'
290 args.generate_c_code is not None or args.generate_docbook is not None
291 ) and args.output is not None:
293 "Using --generate-c-code or --generate-docbook and "
294 "--output at the same time is not allowed"
297 if args.generate_c_code:
298 header_name = args.generate_c_code + ".h"
299 h_file = os.path.join(args.output_directory, header_name)
301 c_file = os.path.join(args.output_directory, args.generate_c_code + ".c")
304 if args.output is None:
305 print_error("Using --header requires --output")
308 header_name = os.path.basename(h_file)
310 if args.output is None:
311 print_error("Using --body requires --output")
314 header_name = os.path.splitext(os.path.basename(c_file))[0] + ".h"
315 elif args.interface_info_header:
316 if args.output is None:
317 print_error("Using --interface-info-header requires --output")
318 if args.c_generate_object_manager:
320 "--c-generate-object-manager is incompatible with "
321 "--interface-info-header"
325 header_name = os.path.basename(h_file)
326 elif args.interface_info_body:
327 if args.output is None:
328 print_error("Using --interface-info-body requires --output")
329 if args.c_generate_object_manager:
331 "--c-generate-object-manager is incompatible with "
332 "--interface-info-body"
336 header_name = os.path.splitext(os.path.basename(c_file))[0] + ".h"
338 # Check the minimum GLib version. The minimum --glib-min-required is 2.30,
339 # because that’s when gdbus-codegen was introduced. Support 1, 2 or 3
340 # component versions, but ignore the micro component if it’s present.
341 if args.glib_min_required:
343 parts = args.glib_min_required.split(".", 3)
344 glib_min_required = (int(parts[0]), int(parts[1] if len(parts) > 1 else 0))
345 # Ignore micro component, but still validate it:
346 _ = int(parts[2] if len(parts) > 2 else 0) # noqa: F841
347 except (ValueError, IndexError):
349 "Unrecognized --glib-min-required string ‘{}’".format(
350 args.glib_min_required
354 if glib_min_required < (2, 30):
356 "Invalid --glib-min-required string ‘{}’: minimum "
357 "version is 2.30".format(args.glib_min_required)
360 glib_min_required = (2, 30)
362 # And the maximum GLib version.
363 if args.glib_max_allowed:
365 parts = args.glib_max_allowed.split(".", 3)
366 glib_max_allowed = (int(parts[0]), int(parts[1] if len(parts) > 1 else 0))
367 # Ignore micro component, but still validate it:
368 _ = int(parts[2] if len(parts) > 2 else 0) # noqa: F841
369 except (ValueError, IndexError):
371 "Unrecognized --glib-max-allowed string ‘{}’".format(
372 args.glib_max_allowed
376 glib_max_allowed = (config.MAJOR_VERSION, config.MINOR_VERSION)
378 # Only allow --symbol-decorator-define and --symbol-decorator-header if
379 # --symbol-decorator is used
380 if args.symbol_decorator is None:
381 if args.symbol_decorator_header or args.symbol_decorator_define:
383 "--symbol-decorator-define and --symbol-decorator-header must "
384 "be used with --symbol-decorator"
387 # Round --glib-max-allowed up to the next stable release.
390 glib_max_allowed[1] + (glib_max_allowed[1] % 2),
393 if glib_max_allowed < glib_min_required:
395 "Invalid versions: --glib-min-required ({}) must be "
396 "less than or equal to --glib-max-allowed ({})".format(
397 glib_min_required, glib_max_allowed
402 input_files_basenames = []
403 for fname in sorted(args.files + args.xml_files):
404 with open(fname, "rb") as f:
406 parsed_ifaces = parser.parse_dbus_xml(
407 xml_data, h_type_implies_unix_fd=(glib_min_required >= (2, 64))
409 all_ifaces.extend(parsed_ifaces)
410 input_files_basenames.append(os.path.basename(fname))
412 if args.annotate is not None:
413 apply_annotations(all_ifaces, args.annotate)
416 i.post_process(args.interface_prefix, args.c_namespace)
418 docbook = args.generate_docbook
419 docbook_gen = codegen_docbook.DocbookCodeGenerator(all_ifaces)
421 docbook_gen.generate(docbook, args.output_directory)
424 with open(h_file, "w") as outfile:
425 gen = codegen.HeaderCodeGenerator(
428 args.c_generate_object_manager,
429 args.c_generate_autocleanup,
431 input_files_basenames,
434 args.symbol_decorator,
435 args.symbol_decorator_header,
441 with open(c_file, "w") as outfile:
442 gen = codegen.CodeGenerator(
445 args.c_generate_object_manager,
447 input_files_basenames,
450 args.symbol_decorator_define,
455 if args.interface_info_header:
456 with open(h_file, "w") as outfile:
457 gen = codegen.InterfaceInfoHeaderCodeGenerator(
461 input_files_basenames,
464 args.symbol_decorator,
465 args.symbol_decorator_header,
470 if args.interface_info_body:
471 with open(c_file, "w") as outfile:
472 gen = codegen.InterfaceInfoBodyCodeGenerator(
476 input_files_basenames,
478 args.symbol_decorator_define,
486 if __name__ == "__main__":