3 # GObject-Introspection - a framework for introspecting GObject libraries
4 # Copyright (C) 2008-2010 Johan Dahlin
5 # Copyright (C) 2009 Red Hat, Inc.
7 # This program is free software; you can redistribute it and/or
8 # modify it under the terms of the GNU General Public License
9 # as published by the Free Software Foundation; either version 2
10 # of the License, or (at your option) any later version.
12 # This program is distributed in the hope that it will be useful,
13 # but WITHOUT ANY WARRANTY; without even the implied warranty of
14 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 # GNU General Public License for more details.
17 # You should have received a copy of the GNU General Public License
18 # along with this program; if not, write to the Free Software
19 # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
31 from giscanner import message
32 from giscanner.annotationparser import AnnotationParser
33 from giscanner.ast import Include, Namespace
34 from giscanner.dumper import compile_introspection_binary
35 from giscanner.gdumpparser import GDumpParser, IntrospectionBinary
36 from giscanner.introspectablepass import IntrospectablePass
37 from giscanner.girparser import GIRParser
38 from giscanner.girwriter import GIRWriter
39 from giscanner.maintransformer import MainTransformer
40 from giscanner.shlibs import resolve_shlibs
41 from giscanner.sourcescanner import SourceScanner
42 from giscanner.transformer import Transformer
45 def _get_option_parser():
46 parser = optparse.OptionParser('%prog [options] sources')
47 parser.add_option('', "--quiet",
48 action="store_true", dest="quiet",
50 help="If passed, do not print details of normal" \
52 parser.add_option("", "--format",
53 action="store", dest="format",
55 help="format to use, one of gidl, gir")
56 parser.add_option("-i", "--include",
57 action="append", dest="includes", default=[],
58 help="Add specified gir file as dependency")
59 parser.add_option("", "--include-uninstalled",
60 action="append", dest="includes_uninstalled", default=[],
61 help=("""A file path to a dependency; only use this "
62 "when building multiple .gir files inside a "
64 parser.add_option("", "--add-include-path",
65 action="append", dest="include_paths", default=[],
66 help="include paths for other GIR files")
67 parser.add_option("", "--program",
68 action="store", dest="program", default=None,
69 help="program to execute")
70 parser.add_option("", "--program-arg",
71 action="append", dest="program_args", default=[],
72 help="extra arguments to program")
73 parser.add_option("", "--libtool",
74 action="store", dest="libtool_path", default=None,
75 help="full path to libtool")
76 parser.add_option("", "--no-libtool",
77 action="store_true", dest="nolibtool", default=False,
78 help="do not use libtool")
79 parser.add_option("-l", "--library",
80 action="append", dest="libraries", default=[],
81 help="libraries of this unit")
82 parser.add_option("-L", "--library-path",
83 action="append", dest="library_paths", default=[],
84 help="directories to search for libraries")
85 parser.add_option("-n", "--namespace",
86 action="store", dest="namespace_name",
87 help=("name of namespace for this unit, also "
88 "used to compute --identifier-prefix and --symbol-prefix"))
89 parser.add_option("", "--nsversion",
90 action="store", dest="namespace_version",
91 help="version of namespace for this unit")
92 parser.add_option("", "--strip-prefix",
93 action="store", dest="strip_prefix",
94 help="""Option --strip-prefix is deprecated, please see --identifier-prefix
95 and --symbol-prefix.""")
96 parser.add_option("", "--identifier-prefix",
97 action="append", dest="identifier_prefixes", default=[],
98 help="""Remove this prefix from C identifiers (structure typedefs, etc.).
99 May be specified multiple times. This is also used as the default for --symbol-prefix if
100 the latter is not specified.""")
101 parser.add_option("", "--symbol-prefix",
102 action="append", dest="symbol_prefixes", default=[],
103 help="Remove this prefix from C symbols (function names)")
104 parser.add_option("", "--accept-unprefixed",
105 action="store_true", dest="accept_unprefixed", default=False,
106 help="""If specified, accept symbols and identifiers that do not
107 match the namespace prefix.""")
108 parser.add_option("", "--add-init-section",
109 action="append", dest="init_sections", default=[],
110 help="add extra initialization code in the introspection program")
111 parser.add_option("-o", "--output",
112 action="store", dest="output", default="-",
113 help="output filename to write to, defaults to - (stdout)")
114 parser.add_option("", "--pkg",
115 action="append", dest="packages", default=[],
116 help="pkg-config packages to get cflags from")
117 parser.add_option("", "--pkg-export",
118 action="append", dest="packages_export", default=[],
119 help="Associated pkg-config packages for this library")
120 parser.add_option('', "--warn-all",
121 action="store_true", dest="warn_all", default=False,
122 help="If true, enable all warnings for introspection")
123 parser.add_option('', "--warn-error",
124 action="store_true", dest="warn_fatal",
125 help="Turn warnings into fatal errors")
126 parser.add_option("-v", "--verbose",
127 action="store_true", dest="verbose",
129 parser.add_option("", "--c-include",
130 action="append", dest="c_includes", default=[],
131 help="headers which should be included in C programs")
133 group = optparse.OptionGroup(parser, "Preprocessor options")
134 group.add_option("-I", help="Pre-processor include file",
135 action="append", dest="cpp_includes",
137 group.add_option("-D", help="Pre-processor define",
138 action="append", dest="cpp_defines",
140 group.add_option("-U", help="Pre-processor undefine",
141 action="append", dest="cpp_undefines",
143 group.add_option("-p", dest="", help="Ignored")
144 parser.add_option_group(group)
147 parser.add_option('', "--generate-typelib-tests",
148 action="store", dest="test_codegen", default=None,
149 help=optparse.SUPPRESS_HELP)
150 parser.add_option('', "--passthrough-gir",
151 action="store", dest="passthrough_gir", default=None,
152 help=optparse.SUPPRESS_HELP)
153 parser.add_option('', "--reparse-validate",
154 action="store_true", dest="reparse_validate_gir", default=False,
155 help=optparse.SUPPRESS_HELP)
156 parser.add_option("", "--typelib-xml",
157 action="store_true", dest="typelib_xml",
158 help=optparse.SUPPRESS_HELP)
164 raise SystemExit('ERROR: %s' % (msg, ))
166 def passthrough_gir(path, f):
170 writer = GIRWriter(parser.get_namespace(),
171 parser.get_shared_libraries(),
172 parser.get_includes(),
173 parser.get_pkgconfig_packages(),
174 parser.get_c_includes())
175 f.write(writer.get_xml())
177 def test_codegen(optstring):
178 (namespace, out_h_filename, out_c_filename) = optstring.split(',')
179 if namespace == 'Everything':
180 from .testcodegen import EverythingCodeGenerator
181 gen = EverythingCodeGenerator(out_h_filename, out_c_filename)
184 _error("Invaild namespace %r" % (namespace, ))
187 def process_options(output, allowed_flags):
188 for option in output.split():
189 for flag in allowed_flags:
190 if not option.startswith(flag):
195 def process_packages(options, packages):
196 args = ['pkg-config', '--cflags']
197 args.extend(packages)
198 output = subprocess.Popen(args,
199 stdout=subprocess.PIPE).communicate()[0]
201 # the error output should have already appeared on our stderr,
204 # Some pkg-config files on Windows have options we don't understand,
205 # so we explicitly filter to only the ones we need.
206 options_whitelist = ['-I', '-D', '-U', '-l', '-L']
207 filtered_output = list(process_options(output, options_whitelist))
208 parser = _get_option_parser()
209 pkg_options, unused = parser.parse_args(filtered_output)
210 options.cpp_includes.extend(pkg_options.cpp_includes)
211 options.cpp_defines.extend(pkg_options.cpp_defines)
212 options.cpp_undefines.extend(pkg_options.cpp_undefines)
214 args = ['pkg-config', '--libs-only-L']
215 args.extend(packages)
216 output = subprocess.Popen(args,
217 stdout=subprocess.PIPE).communicate()[0]
220 filtered_output = list(process_options(output, options_whitelist))
221 pkg_options, unused = parser.parse_args(filtered_output)
222 options.library_paths.extend(pkg_options.library_paths)
224 def scanner_main(args):
225 parser = _get_option_parser()
226 (options, args) = parser.parse_args(args)
228 if options.passthrough_gir:
229 passthrough_gir(options.passthrough_gir, sys.stdout)
230 if options.test_codegen:
231 return test_codegen(options.test_codegen)
234 _error('Need at least one filename')
236 if not options.namespace_name:
237 _error('Namespace name missing')
239 if options.format == 'gir':
240 from giscanner.girwriter import GIRWriter as Writer
242 _error("Unknown format: %s" % (options.format, ))
244 if not (options.libraries or options.program):
245 _error("Must specify --program or --library")
246 libraries = options.libraries
248 if options.strip_prefix:
249 print """g-ir-scanner: warning: Option --strip-prefix has been deprecated;
250 see --identifier-prefix and --symbol-prefix."""
251 options.identifier_prefixes.append(options.strip_prefix)
255 # We don't support real C++ parsing yet, but we should be able
256 # to understand C API implemented in C++ files.
257 if (arg.endswith('.c') or arg.endswith('.cpp') or
258 arg.endswith('.cc') or arg.endswith('.cxx') or
259 arg.endswith('.h') or arg.endswith('.hpp') or
260 arg.endswith('.hxx')):
261 if not os.path.exists(arg):
262 _error('%s: no such a file or directory' % (arg, ))
263 # Make absolute, because we do comparisons inside scannerparser.c
264 # against the absolute path that cpp will give us
265 filenames.append(os.path.abspath(arg))
267 # We do this dance because the empty list has different semantics from
268 # None; if the user didn't specify the options, we want to use None so
269 # the Namespace constructor picks the defaults.
270 if options.identifier_prefixes:
271 identifier_prefixes = options.identifier_prefixes
273 identifier_prefixes = None
274 if options.symbol_prefixes:
275 for prefix in options.symbol_prefixes:
276 # See Transformer._split_c_string_for_namespace_matches() for
277 # why this check is needed
278 if prefix.lower() != prefix:
279 _error("Values for --symbol-prefix must be entirely lowercase")
280 symbol_prefixes = options.symbol_prefixes
282 symbol_prefixes = None
284 namespace = Namespace(options.namespace_name,
285 options.namespace_version,
286 identifier_prefixes=identifier_prefixes,
287 symbol_prefixes=symbol_prefixes)
288 logger = message.MessageLogger.get(namespace=namespace)
290 logger.enable_warnings(True)
291 transformer = Transformer(namespace,
292 accept_unprefixed=options.accept_unprefixed)
293 transformer.set_include_paths(options.include_paths)
294 shown_include_warning = False
295 for include in options.includes:
296 if os.sep in include:
297 _error("Invalid include path %r" % (include, ))
299 include_obj = Include.from_string(include)
301 _error("Malformed include %r\n" % (include, ))
302 transformer.register_include(include_obj)
303 for include_path in options.includes_uninstalled:
304 transformer.register_include_uninstalled(include_path)
306 packages = set(options.packages)
307 packages.update(transformer.get_pkgconfig_packages())
308 exit_code = process_packages(options, packages)
312 # Run the preprocessor, tokenize and construct simple
313 # objects representing the raw C symbols
315 ss.set_cpp_options(options.cpp_includes,
317 options.cpp_undefines)
318 ss.parse_files(filenames)
319 ss.parse_macros(filenames)
321 # Transform the C symbols into AST nodes
322 transformer.parse(ss.get_symbols())
324 # Transform the C AST nodes into higher level
326 gdump_parser = GDumpParser(transformer)
328 # Do enough parsing that we have the get_type() functions to reference
329 # when creating the introspection binary
330 gdump_parser.init_parse()
333 args=[options.program]
334 args.extend(options.program_args)
335 binary = IntrospectionBinary(args)
337 binary = compile_introspection_binary(options,
338 gdump_parser.get_get_type_functions())
340 shlibs = resolve_shlibs(options, binary, libraries)
342 gdump_parser.set_introspection_binary(binary)
345 ap = AnnotationParser()
346 blocks = ap.parse(ss.get_comments())
348 main = MainTransformer(transformer, blocks)
351 utils.break_on_debug_flag('tree')
353 final = IntrospectablePass(transformer)
356 if options.warn_fatal and logger.did_warn():
357 message.fatal("warnings configured as fatal")
361 if options.packages_export:
362 exported_packages = options.packages_export
364 exported_packages = options.packages
365 writer = Writer(transformer.namespace, shlibs, transformer.get_includes(),
366 exported_packages, options.c_includes)
367 data = writer.get_xml()
369 if options.output == "-":
371 elif options.reparse_validate_gir:
372 main_f, main_f_name = tempfile.mkstemp(suffix='.gir')
373 main_f = os.fdopen(main_f, 'w')
377 temp_f, temp_f_name = tempfile.mkstemp(suffix='.gir')
378 temp_f = os.fdopen(temp_f, 'w')
379 passthrough_gir(main_f_name, temp_f)
381 if not utils.files_are_identical(main_f_name, temp_f_name):
382 _error("Failed to re-parse gir file; scanned=%r passthrough=%r" % (
383 main_f_name, temp_f_name))
384 os.unlink(temp_f_name)
386 shutil.move(main_f_name, options.output)
388 if e.errno == errno.EPERM:
389 os.unlink(main_f_name)
395 output = open(options.output, "w")
397 _error("opening output for writing: %s" % (e.strerror, ))
402 _error("while writing output: %s" % (e.strerror, ))