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_preprocessor_option_group(parser):
46 group = optparse.OptionGroup(parser, "Preprocessor options")
47 group.add_option("-I", help="Pre-processor include file",
48 action="append", dest="cpp_includes",
50 group.add_option("-D", help="Pre-processor define",
51 action="append", dest="cpp_defines",
53 group.add_option("-U", help="Pre-processor undefine",
54 action="append", dest="cpp_undefines",
56 group.add_option("-p", dest="", help="Ignored")
59 def _get_option_parser():
60 parser = optparse.OptionParser('%prog [options] sources')
61 parser.add_option('', "--quiet",
62 action="store_true", dest="quiet",
64 help="If passed, do not print details of normal" \
66 parser.add_option("", "--format",
67 action="store", dest="format",
69 help="format to use, one of gidl, gir")
70 parser.add_option("-i", "--include",
71 action="append", dest="includes", default=[],
72 help="Add specified gir file as dependency")
73 parser.add_option("", "--include-uninstalled",
74 action="append", dest="includes_uninstalled", default=[],
75 help=("""A file path to a dependency; only use this "
76 "when building multiple .gir files inside a "
78 parser.add_option("", "--add-include-path",
79 action="append", dest="include_paths", default=[],
80 help="include paths for other GIR files")
81 parser.add_option("", "--program",
82 action="store", dest="program", default=None,
83 help="program to execute")
84 parser.add_option("", "--program-arg",
85 action="append", dest="program_args", default=[],
86 help="extra arguments to program")
87 parser.add_option("", "--libtool",
88 action="store", dest="libtool_path", default=None,
89 help="full path to libtool")
90 parser.add_option("", "--no-libtool",
91 action="store_true", dest="nolibtool", default=False,
92 help="do not use libtool")
93 parser.add_option("-l", "--library",
94 action="append", dest="libraries", default=[],
95 help="libraries of this unit")
96 parser.add_option("-L", "--library-path",
97 action="append", dest="library_paths", default=[],
98 help="directories to search for libraries")
99 parser.add_option("-n", "--namespace",
100 action="store", dest="namespace_name",
101 help=("name of namespace for this unit, also "
102 "used to compute --identifier-prefix and --symbol-prefix"))
103 parser.add_option("", "--nsversion",
104 action="store", dest="namespace_version",
105 help="version of namespace for this unit")
106 parser.add_option("", "--strip-prefix",
107 action="store", dest="strip_prefix",
108 help="""Option --strip-prefix is deprecated, please see --identifier-prefix
109 and --symbol-prefix.""")
110 parser.add_option("", "--identifier-prefix",
111 action="append", dest="identifier_prefixes", default=[],
112 help="""Remove this prefix from C identifiers (structure typedefs, etc.).
113 May be specified multiple times. This is also used as the default for --symbol-prefix if
114 the latter is not specified.""")
115 parser.add_option("", "--symbol-prefix",
116 action="append", dest="symbol_prefixes", default=[],
117 help="Remove this prefix from C symbols (function names)")
118 parser.add_option("", "--accept-unprefixed",
119 action="store_true", dest="accept_unprefixed", default=False,
120 help="""If specified, accept symbols and identifiers that do not
121 match the namespace prefix.""")
122 parser.add_option("", "--add-init-section",
123 action="append", dest="init_sections", default=[],
124 help="add extra initialization code in the introspection program")
125 parser.add_option("-o", "--output",
126 action="store", dest="output", default="-",
127 help="output filename to write to, defaults to - (stdout)")
128 parser.add_option("", "--pkg",
129 action="append", dest="packages", default=[],
130 help="pkg-config packages to get cflags from")
131 parser.add_option("", "--pkg-export",
132 action="append", dest="packages_export", default=[],
133 help="Associated pkg-config packages for this library")
134 parser.add_option('', "--warn-all",
135 action="store_true", dest="warn_all", default=False,
136 help="If true, enable all warnings for introspection")
137 parser.add_option('', "--warn-error",
138 action="store_true", dest="warn_fatal",
139 help="Turn warnings into fatal errors")
140 parser.add_option("-v", "--verbose",
141 action="store_true", dest="verbose",
143 parser.add_option("", "--c-include",
144 action="append", dest="c_includes", default=[],
145 help="headers which should be included in C programs")
147 group = get_preprocessor_option_group(parser)
148 parser.add_option_group(group)
151 parser.add_option('', "--generate-typelib-tests",
152 action="store", dest="test_codegen", default=None,
153 help=optparse.SUPPRESS_HELP)
154 parser.add_option('', "--passthrough-gir",
155 action="store", dest="passthrough_gir", default=None,
156 help=optparse.SUPPRESS_HELP)
157 parser.add_option('', "--reparse-validate",
158 action="store_true", dest="reparse_validate_gir", default=False,
159 help=optparse.SUPPRESS_HELP)
160 parser.add_option("", "--typelib-xml",
161 action="store_true", dest="typelib_xml",
162 help=optparse.SUPPRESS_HELP)
168 raise SystemExit('ERROR: %s' % (msg, ))
170 def passthrough_gir(path, f):
174 writer = GIRWriter(parser.get_namespace(),
175 parser.get_shared_libraries(),
176 parser.get_includes(),
177 parser.get_pkgconfig_packages(),
178 parser.get_c_includes())
179 f.write(writer.get_xml())
181 def test_codegen(optstring):
182 (namespace, out_h_filename, out_c_filename) = optstring.split(',')
183 if namespace == 'Everything':
184 from .testcodegen import EverythingCodeGenerator
185 gen = EverythingCodeGenerator(out_h_filename, out_c_filename)
188 _error("Invaild namespace %r" % (namespace, ))
191 def process_options(output, allowed_flags):
192 for option in output.split():
193 for flag in allowed_flags:
194 if not option.startswith(flag):
199 def process_packages(options, packages):
200 args = ['pkg-config', '--cflags']
201 args.extend(packages)
202 output = subprocess.Popen(args,
203 stdout=subprocess.PIPE).communicate()[0]
205 # the error output should have already appeared on our stderr,
208 # Some pkg-config files on Windows have options we don't understand,
209 # so we explicitly filter to only the ones we need.
210 options_whitelist = ['-I', '-D', '-U', '-l', '-L']
211 filtered_output = list(process_options(output, options_whitelist))
212 parser = _get_option_parser()
213 pkg_options, unused = parser.parse_args(filtered_output)
214 options.cpp_includes.extend(pkg_options.cpp_includes)
215 options.cpp_defines.extend(pkg_options.cpp_defines)
216 options.cpp_undefines.extend(pkg_options.cpp_undefines)
218 def extract_filenames(args):
221 # We don't support real C++ parsing yet, but we should be able
222 # to understand C API implemented in C++ files.
223 if (arg.endswith('.c') or arg.endswith('.cpp') or
224 arg.endswith('.cc') or arg.endswith('.cxx') or
225 arg.endswith('.h') or arg.endswith('.hpp') or
226 arg.endswith('.hxx')):
227 if not os.path.exists(arg):
228 _error('%s: no such a file or directory' % (arg, ))
229 # Make absolute, because we do comparisons inside scannerparser.c
230 # against the absolute path that cpp will give us
231 filenames.append(os.path.abspath(arg))
234 def create_namespace(options):
235 if options.strip_prefix:
236 print """g-ir-scanner: warning: Option --strip-prefix has been deprecated;
237 see --identifier-prefix and --symbol-prefix."""
238 options.identifier_prefixes.append(options.strip_prefix)
240 # We do this dance because the empty list has different semantics from
241 # None; if the user didn't specify the options, we want to use None so
242 # the Namespace constructor picks the defaults.
243 if options.identifier_prefixes:
244 identifier_prefixes = options.identifier_prefixes
246 identifier_prefixes = None
247 if options.symbol_prefixes:
248 for prefix in options.symbol_prefixes:
249 # See Transformer._split_c_string_for_namespace_matches() for
250 # why this check is needed
251 if prefix.lower() != prefix:
252 _error("Values for --symbol-prefix must be entirely lowercase")
253 symbol_prefixes = options.symbol_prefixes
255 symbol_prefixes = None
257 return Namespace(options.namespace_name,
258 options.namespace_version,
259 identifier_prefixes=identifier_prefixes,
260 symbol_prefixes=symbol_prefixes)
262 def create_transformer(namespace, options):
263 transformer = Transformer(namespace,
264 accept_unprefixed=options.accept_unprefixed)
265 transformer.set_include_paths(options.include_paths)
266 if options.passthrough_gir:
267 transformer.disable_cache()
268 transformer.set_passthrough_mode()
270 shown_include_warning = False
271 for include in options.includes:
272 if os.sep in include:
273 _error("Invalid include path %r" % (include, ))
275 include_obj = Include.from_string(include)
277 _error("Malformed include %r\n" % (include, ))
278 transformer.register_include(include_obj)
279 for include_path in options.includes_uninstalled:
280 transformer.register_include_uninstalled(include_path)
284 def create_binary(transformer, options, args):
285 # Transform the C AST nodes into higher level
287 gdump_parser = GDumpParser(transformer)
289 # Do enough parsing that we have the get_type() functions to reference
290 # when creating the introspection binary
291 gdump_parser.init_parse()
294 args=[options.program]
295 args.extend(options.program_args)
296 binary = IntrospectionBinary(args)
298 binary = compile_introspection_binary(options,
299 gdump_parser.get_get_type_functions())
301 shlibs = resolve_shlibs(options, binary, options.libraries)
302 gdump_parser.set_introspection_binary(binary)
306 def create_source_scanner(options, args):
307 filenames = extract_filenames(args)
309 # Run the preprocessor, tokenize and construct simple
310 # objects representing the raw C symbols
312 ss.set_cpp_options(options.cpp_includes,
314 options.cpp_undefines)
315 ss.parse_files(filenames)
316 ss.parse_macros(filenames)
319 def write_output(data, options):
320 if options.output == "-":
322 elif options.reparse_validate_gir:
323 main_f, main_f_name = tempfile.mkstemp(suffix='.gir')
324 main_f = os.fdopen(main_f, 'w')
328 temp_f, temp_f_name = tempfile.mkstemp(suffix='.gir')
329 temp_f = os.fdopen(temp_f, 'w')
330 passthrough_gir(main_f_name, temp_f)
332 if not utils.files_are_identical(main_f_name, temp_f_name):
333 _error("Failed to re-parse gir file; scanned=%r passthrough=%r" % (
334 main_f_name, temp_f_name))
335 os.unlink(temp_f_name)
337 shutil.move(main_f_name, options.output)
339 if e.errno == errno.EPERM:
340 os.unlink(main_f_name)
346 output = open(options.output, "w")
348 _error("opening output for writing: %s" % (e.strerror, ))
353 _error("while writing output: %s" % (e.strerror, ))
355 def scanner_main(args):
356 parser = _get_option_parser()
357 (options, args) = parser.parse_args(args)
359 if options.passthrough_gir:
360 passthrough_gir(options.passthrough_gir, sys.stdout)
361 if options.test_codegen:
362 return test_codegen(options.test_codegen)
365 _error('Need at least one filename')
367 if not options.namespace_name:
368 _error('Namespace name missing')
370 if options.format == 'gir':
371 from giscanner.girwriter import GIRWriter as Writer
373 _error("Unknown format: %s" % (options.format, ))
375 if not (options.libraries or options.program):
376 _error("Must specify --program or --library")
378 namespace = create_namespace(options)
379 logger = message.MessageLogger.get(namespace=namespace)
381 logger.enable_warnings(True)
382 transformer = create_transformer(namespace, options)
384 packages = set(options.packages)
385 packages.update(transformer.get_pkgconfig_packages())
386 exit_code = process_packages(options, packages)
390 ss = create_source_scanner(options, args)
392 # Transform the C symbols into AST nodes
393 transformer.parse(ss.get_symbols())
395 shlibs = create_binary(transformer, options, args)
397 ap = AnnotationParser()
398 blocks = ap.parse(ss.get_comments())
400 main = MainTransformer(transformer, blocks)
403 utils.break_on_debug_flag('tree')
405 final = IntrospectablePass(transformer, blocks)
408 warning_count = logger.get_warning_count()
409 if options.warn_fatal and warning_count > 0:
410 message.fatal("warnings configured as fatal")
412 elif warning_count > 0:
413 print ("g-ir-scanner: %s: warning: %d warnings suppressed (use --warn-all to see them)"
414 % (transformer.namespace.name, warning_count, ))
417 if options.packages_export:
418 exported_packages = options.packages_export
420 exported_packages = options.packages
422 writer = Writer(transformer.namespace, shlibs, transformer.get_includes(),
423 exported_packages, options.c_includes)
424 data = writer.get_xml()
426 write_output(data, options)