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
43 from giscanner.utils import files_are_identical
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("", "--identifier-prefix",
93 action="append", dest="identifier_prefixes", default=[],
94 help="""Remove this prefix from C identifiers (structure typedefs, etc.).
95 May be specified multiple times. This is also used as the default for --symbol-prefix if
96 the latter is not specified.""")
97 parser.add_option("", "--symbol-prefix",
98 action="append", dest="symbol_prefixes", default=[],
99 help="Remove this prefix from C symbols (function names)")
100 parser.add_option("", "--accept-unprefixed",
101 action="store_true", dest="accept_unprefixed", default=False,
102 help="""If specified, accept symbols and identifiers that do not
103 match the namespace prefix.""")
104 parser.add_option("", "--add-init-section",
105 action="append", dest="init_sections", default=[],
106 help="add extra initialization code in the introspection program")
107 parser.add_option("-o", "--output",
108 action="store", dest="output", default="-",
109 help="output filename to write to, defaults to - (stdout)")
110 parser.add_option("", "--pkg",
111 action="append", dest="packages", default=[],
112 help="pkg-config packages to get cflags from")
113 parser.add_option("", "--pkg-export",
114 action="append", dest="packages_export", default=[],
115 help="Associated pkg-config packages for this library")
116 parser.add_option('', "--warn-all",
117 action="store_true", dest="warn_all", default=False,
118 help="If true, enable all warnings for introspection")
119 parser.add_option('', "--warn-error",
120 action="store_true", dest="warn_fatal",
121 help="Turn warnings into fatal errors")
122 parser.add_option("-v", "--verbose",
123 action="store_true", dest="verbose",
125 parser.add_option("", "--c-include",
126 action="append", dest="c_includes", default=[],
127 help="headers which should be included in C programs")
129 group = optparse.OptionGroup(parser, "Preprocessor options")
130 group.add_option("-I", help="Pre-processor include file",
131 action="append", dest="cpp_includes",
133 group.add_option("-D", help="Pre-processor define",
134 action="append", dest="cpp_defines",
136 group.add_option("-U", help="Pre-processor undefine",
137 action="append", dest="cpp_undefines",
139 group.add_option("-p", dest="", help="Ignored")
140 parser.add_option_group(group)
143 parser.add_option('', "--generate-typelib-tests",
144 action="store", dest="test_codegen", default=None,
145 help=optparse.SUPPRESS_HELP)
146 parser.add_option('', "--passthrough-gir",
147 action="store", dest="passthrough_gir", default=None,
148 help=optparse.SUPPRESS_HELP)
149 parser.add_option('', "--reparse-validate",
150 action="store_true", dest="reparse_validate_gir", default=False,
151 help=optparse.SUPPRESS_HELP)
152 parser.add_option("", "--typelib-xml",
153 action="store_true", dest="typelib_xml",
154 help=optparse.SUPPRESS_HELP)
160 raise SystemExit('ERROR: %s' % (msg, ))
162 def passthrough_gir(path, f):
166 writer = GIRWriter(parser.get_namespace(),
167 parser.get_shared_libraries(),
168 parser.get_includes(),
169 parser.get_pkgconfig_packages(),
170 parser.get_c_includes())
171 f.write(writer.get_xml())
173 def test_codegen(optstring):
174 (namespace, out_h_filename, out_c_filename) = optstring.split(',')
175 if namespace == 'Everything':
176 from .testcodegen import EverythingCodeGenerator
177 gen = EverythingCodeGenerator(out_h_filename, out_c_filename)
180 _error("Invaild namespace %r" % (namespace, ))
183 def process_options(output, allowed_flags):
184 for option in output.split():
185 for flag in allowed_flags:
186 if not option.startswith(flag):
191 def process_packages(options, packages):
192 args = ['pkg-config', '--cflags']
193 args.extend(packages)
194 output = subprocess.Popen(args,
195 stdout=subprocess.PIPE).communicate()[0]
197 # the error output should have already appeared on our stderr,
200 # Some pkg-config files on Windows have options we don't understand,
201 # so we explicitly filter to only the ones we need.
202 options_whitelist = ['-I', '-D', '-U', '-l', '-L']
203 filtered_output = list(process_options(output, options_whitelist))
204 parser = _get_option_parser()
205 pkg_options, unused = parser.parse_args(filtered_output)
206 options.cpp_includes.extend(pkg_options.cpp_includes)
207 options.cpp_defines.extend(pkg_options.cpp_defines)
208 options.cpp_undefines.extend(pkg_options.cpp_undefines)
210 args = ['pkg-config', '--libs-only-L']
211 args.extend(packages)
212 output = subprocess.Popen(args,
213 stdout=subprocess.PIPE).communicate()[0]
216 filtered_output = list(process_options(output, options_whitelist))
217 pkg_options, unused = parser.parse_args(filtered_output)
218 options.library_paths.extend(pkg_options.library_paths)
220 def scanner_main(args):
221 parser = _get_option_parser()
222 (options, args) = parser.parse_args(args)
224 if options.passthrough_gir:
225 passthrough_gir(options.passthrough_gir, sys.stdout)
226 if options.test_codegen:
227 return test_codegen(options.test_codegen)
230 _error('Need at least one filename')
232 if not options.namespace_name:
233 _error('Namespace name missing')
235 if options.format == 'gir':
236 from giscanner.girwriter import GIRWriter as Writer
238 _error("Unknown format: %s" % (options.format, ))
240 if not (options.libraries or options.program):
241 _error("Must specify --program or --library")
242 libraries = options.libraries
246 # We don't support real C++ parsing yet, but we should be able
247 # to understand C API implemented in C++ files.
248 if (arg.endswith('.c') or arg.endswith('.cpp') or
249 arg.endswith('.cc') or arg.endswith('.cxx') or
250 arg.endswith('.h') or arg.endswith('.hpp') or
251 arg.endswith('.hxx')):
252 if not os.path.exists(arg):
253 _error('%s: no such a file or directory' % (arg, ))
254 # Make absolute, because we do comparisons inside scannerparser.c
255 # against the absolute path that cpp will give us
256 filenames.append(os.path.abspath(arg))
258 # We do this dance because the empty list has different semantics from
259 # None; if the user didn't specify the options, we want to use None so
260 # the Namespace constructor picks the defaults.
261 if options.identifier_prefixes:
262 identifier_prefixes = options.identifier_prefixes
264 identifier_prefixes = None
265 if options.symbol_prefixes:
266 symbol_prefixes = options.symbol_prefixes
268 symbol_prefixes = None
270 namespace = Namespace(options.namespace_name,
271 options.namespace_version,
272 identifier_prefixes=identifier_prefixes,
273 symbol_prefixes=symbol_prefixes)
274 logger = message.MessageLogger.get(namespace=namespace)
276 logger.enable_warnings(True)
277 transformer = Transformer(namespace,
278 accept_unprefixed=options.accept_unprefixed)
279 transformer.set_include_paths(options.include_paths)
280 shown_include_warning = False
281 for include in options.includes:
282 if os.sep in include:
283 _error("Invalid include path %r" % (include, ))
285 include_obj = Include.from_string(include)
287 _error("Malformed include %r\n" % (include, ))
288 transformer.register_include(include_obj)
289 for include_path in options.includes_uninstalled:
290 transformer.register_include_uninstalled(include_path)
292 packages = set(options.packages)
293 packages.update(transformer.get_pkgconfig_packages())
294 exit_code = process_packages(options, packages)
298 # Run the preprocessor, tokenize and construct simple
299 # objects representing the raw C symbols
301 ss.set_cpp_options(options.cpp_includes,
303 options.cpp_undefines)
304 ss.parse_files(filenames)
305 ss.parse_macros(filenames)
307 # Transform the C symbols into AST nodes
308 transformer.parse(ss.get_symbols())
310 # Transform the C AST nodes into higher level
312 gdump_parser = GDumpParser(transformer)
314 # Do enough parsing that we have the get_type() functions to reference
315 # when creating the introspection binary
316 gdump_parser.init_parse()
319 args=[options.program]
320 args.extend(options.program_args)
321 binary = IntrospectionBinary(args)
323 binary = compile_introspection_binary(options,
324 gdump_parser.get_get_type_functions())
326 shlibs = resolve_shlibs(options, binary, libraries)
328 gdump_parser.set_introspection_binary(binary)
331 ap = AnnotationParser()
332 blocks = ap.parse(ss.get_comments())
334 main = MainTransformer(transformer, blocks)
337 final = IntrospectablePass(transformer)
340 if options.warn_fatal and logger.did_warn():
341 message.fatal("warnings configured as fatal")
345 if options.packages_export:
346 exported_packages = options.packages_export
348 exported_packages = options.packages
349 writer = Writer(transformer.namespace, shlibs, transformer.get_includes(),
350 exported_packages, options.c_includes)
351 data = writer.get_xml()
353 if options.output == "-":
355 elif options.reparse_validate_gir:
356 main_f, main_f_name = tempfile.mkstemp(suffix='.gir')
357 main_f = os.fdopen(main_f, 'w')
361 temp_f, temp_f_name = tempfile.mkstemp(suffix='.gir')
362 temp_f = os.fdopen(temp_f, 'w')
363 passthrough_gir(main_f_name, temp_f)
365 if not files_are_identical(main_f_name, temp_f_name):
366 _error("Failed to re-parse gir file; scanned=%r passthrough=%r" % (
367 main_f_name, temp_f_name))
368 os.unlink(temp_f_name)
370 shutil.move(main_f_name, options.output)
372 if e.errno == errno.EPERM:
373 os.unlink(main_f_name)
379 output = open(options.output, "w")
381 _error("opening output for writing: %s" % (e.strerror, ))
386 _error("while writing output: %s" % (e.strerror, ))