• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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