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.annotationparser import AnnotationParser
32 from giscanner.ast import Include
33 from giscanner.dumper import compile_introspection_binary
34 from giscanner.gdumpparser import GDumpParser, IntrospectionBinary
35 from giscanner.maintransformer import MainTransformer
36 from giscanner.introspectablepass import IntrospectablePass
37 from giscanner.girparser import GIRParser
38 from giscanner.girwriter import GIRWriter
39 from giscanner.shlibs import resolve_shlibs
40 from giscanner.sourcescanner import SourceScanner
41 from giscanner.transformer import Transformer
42 from giscanner.utils import files_are_identical
44 def _get_option_parser():
45 parser = optparse.OptionParser('%prog [options] sources')
46 parser.add_option('', "--quiet",
47 action="store_true", dest="quiet",
49 help="If passed, do not print details of normal" \
51 parser.add_option("", "--format",
52 action="store", dest="format",
54 help="format to use, one of gidl, gir")
55 parser.add_option("-i", "--include",
56 action="append", dest="includes", default=[],
57 help="include types for other gidls")
58 parser.add_option("", "--add-include-path",
59 action="append", dest="include_paths", default=[],
60 help="include paths for other GIR files")
61 parser.add_option("", "--program",
62 action="store", dest="program", default=None,
63 help="program to execute")
64 parser.add_option("", "--program-arg",
65 action="append", dest="program_args", default=[],
66 help="extra arguments to program")
67 parser.add_option("", "--libtool",
68 action="store", dest="libtool_path", default=None,
69 help="full path to libtool")
70 parser.add_option("", "--no-libtool",
71 action="store_true", dest="nolibtool", default=False,
72 help="do not use libtool")
73 parser.add_option("-l", "--library",
74 action="append", dest="libraries", default=[],
75 help="libraries of this unit")
76 parser.add_option("-L", "--library-path",
77 action="append", dest="library_paths", default=[],
78 help="directories to search for libraries")
79 parser.add_option("-n", "--namespace",
80 action="store", dest="namespace_name",
81 help=("name of namespace for this unit, also "
82 "used to compute --identifier-prefix and --symbol-prefix"))
83 parser.add_option("", "--nsversion",
84 action="store", dest="namespace_version",
85 help="version of namespace for this unit")
86 parser.add_option("", "--identifier-prefix",
87 action="append", dest="identifier_prefixes", default=[],
88 help="""Remove this prefix from C identifiers (structure typedefs, etc.).
89 May be specified multiple times. This is also used as the default for --symbol-prefix if
90 the latter is not specified.""")
91 parser.add_option("", "--symbol-prefix",
92 action="append", dest="symbol_prefixes", default=[],
93 help="Remove this prefix from C symbols (function names)")
94 parser.add_option("", "--accept-unprefixed",
95 action="store_true", dest="accept_unprefixed", default=False,
96 help="""If specified, accept symbols and identifiers that do not
97 match the namespace prefix.""")
98 parser.add_option("", "--add-init-section",
99 action="append", dest="init_sections", default=[],
100 help="add extra initialization code in the introspection program")
101 parser.add_option("-o", "--output",
102 action="store", dest="output", default="-",
103 help="output filename to write to, defaults to - (stdout)")
104 parser.add_option("", "--pkg",
105 action="append", dest="packages", default=[],
106 help="pkg-config packages to get cflags from")
107 parser.add_option("", "--pkg-export",
108 action="append", dest="packages_export", default=[],
109 help="Associated pkg-config packages for this library")
110 parser.add_option('', "--warn-all",
111 action="store_true", dest="warn_all", default=False,
112 help="If true, enable all warnings for introspection")
113 parser.add_option('', "--warn-error",
114 action="store_true", dest="warn_fatal",
115 help="Turn warnings into fatal errors")
116 parser.add_option("-v", "--verbose",
117 action="store_true", dest="verbose",
119 parser.add_option("", "--c-include",
120 action="append", dest="c_includes", default=[],
121 help="headers which should be included in C programs")
123 group = optparse.OptionGroup(parser, "Preprocessor options")
124 group.add_option("-I", help="Pre-processor include file",
125 action="append", dest="cpp_includes",
127 group.add_option("-D", help="Pre-processor define",
128 action="append", dest="cpp_defines",
130 group.add_option("-U", help="Pre-processor undefine",
131 action="append", dest="cpp_undefines",
133 group.add_option("-p", dest="", help="Ignored")
134 parser.add_option_group(group)
137 parser.add_option('', "--generate-typelib-tests",
138 action="store", dest="test_codegen", default=None,
139 help=optparse.SUPPRESS_HELP)
140 parser.add_option('', "--passthrough-gir",
141 action="store", dest="passthrough_gir", default=None,
142 help=optparse.SUPPRESS_HELP)
143 parser.add_option('', "--reparse-validate",
144 action="store_true", dest="reparse_validate_gir", default=False,
145 help=optparse.SUPPRESS_HELP)
146 parser.add_option("", "--typelib-xml",
147 action="store_true", dest="typelib_xml",
148 help=optparse.SUPPRESS_HELP)
154 raise SystemExit('ERROR: %s' % (msg, ))
156 def passthrough_gir(path, f):
160 writer = GIRWriter(parser.get_namespace(),
161 parser.get_shared_libraries(),
162 parser.get_includes(),
163 parser.get_pkgconfig_packages(),
164 parser.get_c_includes())
165 f.write(writer.get_xml())
167 def test_codegen(optstring):
168 (namespace, out_h_filename, out_c_filename) = optstring.split(',')
169 if namespace == 'Everything':
170 from .testcodegen import EverythingCodeGenerator
171 gen = EverythingCodeGenerator(out_h_filename, out_c_filename)
174 _error("Invaild namespace %r" % (namespace, ))
177 def process_options(output, allowed_flags):
178 for option in output.split():
179 for flag in allowed_flags:
180 if not option.startswith(flag):
185 def process_packages(parser, options, packages):
186 args = ['pkg-config', '--cflags']
187 args.extend(packages)
188 output = subprocess.Popen(args,
189 stdout=subprocess.PIPE).communicate()[0]
191 # the error output should have already appeared on our stderr,
194 # Some pkg-config files on Windows have options we don't understand,
195 # so we explicitly filter to only the ones we need.
196 options_whitelist = ['-I', '-D', '-U', '-l', '-L']
197 filtered_output = list(process_options(output, options_whitelist))
198 pkg_options, unused = parser.parse_args(filtered_output)
199 options.cpp_includes.extend(pkg_options.cpp_includes)
200 options.cpp_defines.extend(pkg_options.cpp_defines)
201 options.cpp_undefines.extend(pkg_options.cpp_undefines)
203 args = ['pkg-config', '--libs-only-L']
204 args.extend(packages)
205 output = subprocess.Popen(args,
206 stdout=subprocess.PIPE).communicate()[0]
209 filtered_output = list(process_options(output, options_whitelist))
210 pkg_options, unused = parser.parse_args(filtered_output)
211 options.library_paths.extend(pkg_options.library_paths)
213 def scanner_main(args):
214 parser = _get_option_parser()
215 (options, args) = parser.parse_args(args)
217 if options.passthrough_gir:
218 passthrough_gir(options.passthrough_gir, sys.stdout)
219 if options.test_codegen:
220 return test_codegen(options.test_codegen)
223 _error('Need at least one filename')
225 if not options.namespace_name:
226 _error('Namespace name missing')
228 if options.format == 'gir':
229 from giscanner.girwriter import GIRWriter as Writer
231 _error("Unknown format: %s" % (options.format, ))
233 if not (options.libraries or options.program):
234 _error("Must specify --program or --library")
235 libraries = options.libraries
239 # We don't support real C++ parsing yet, but we should be able
240 # to understand C API implemented in C++ files.
241 if (arg.endswith('.c') or arg.endswith('.cpp') or
242 arg.endswith('.cc') or arg.endswith('.cxx') or
243 arg.endswith('.h') or arg.endswith('.hpp') or
244 arg.endswith('.hxx')):
245 if not os.path.exists(arg):
246 _error('%s: no such a file or directory' % (arg, ))
247 # Make absolute, because we do comparisons inside scannerparser.c
248 # against the absolute path that cpp will give us
249 filenames.append(os.path.abspath(arg))
251 # We do this dance because the empty list has different semantics from
252 # None; if the user didn't specify the options, we want to use None so
253 # the Namespace constructor picks the defaults.
254 if options.identifier_prefixes:
255 identifier_prefixes = options.identifier_prefixes
257 identifier_prefixes = None
258 if options.symbol_prefixes:
259 symbol_prefixes = options.symbol_prefixes
261 symbol_prefixes = None
263 transformer = Transformer(options.namespace_name,
264 options.namespace_version,
265 identifier_prefixes=identifier_prefixes,
266 symbol_prefixes=symbol_prefixes,
267 accept_unprefixed=options.accept_unprefixed)
269 transformer.enable_warnings(True)
270 transformer.set_include_paths(options.include_paths)
271 shown_include_warning = False
272 for include in options.includes:
273 if os.sep in include:
274 _error("Invalid include path %r" % (include, ))
276 include_obj = Include.from_string(include)
278 _error("Malformed include %r\n" % (include, ))
279 transformer.register_include(include_obj)
281 packages = set(options.packages)
282 packages.update(transformer.get_pkgconfig_packages())
283 exit_code = process_packages(parser, options, packages)
287 # Run the preprocessor, tokenize and construct simple
288 # objects representing the raw C symbols
290 ss.set_cpp_options(options.cpp_includes,
292 options.cpp_undefines)
293 ss.parse_files(filenames)
294 ss.parse_macros(filenames)
296 # Transform the C symbols into AST nodes
297 transformer.set_source_ast(ss)
299 # Transform the C AST nodes into higher level
301 gdump_parser = GDumpParser(transformer)
303 # Do enough parsing that we have the get_type() functions to reference
304 # when creating the introspection binary
305 gdump_parser.init_parse()
308 args=[options.program]
309 args.extend(options.program_args)
310 binary = IntrospectionBinary(args)
312 binary = compile_introspection_binary(options,
313 gdump_parser.get_get_type_functions())
315 shlibs = resolve_shlibs(options, binary, libraries)
317 gdump_parser.set_introspection_binary(binary)
320 ap = AnnotationParser(ss)
323 main = MainTransformer(transformer, blocks)
326 final = IntrospectablePass(transformer)
329 if options.warn_fatal and transformer.did_warn():
330 transformer.log_warning("warnings configured as fatal", fatal=True)
334 if options.packages_export:
335 exported_packages = options.packages_export
337 exported_packages = options.packages
338 writer = Writer(transformer.namespace, shlibs, transformer.get_includes(),
339 exported_packages, options.c_includes)
340 data = writer.get_xml()
342 if options.output == "-":
344 elif options.reparse_validate_gir:
345 main_f = tempfile.NamedTemporaryFile(suffix='.gir', delete=False)
349 temp_f = tempfile.NamedTemporaryFile(suffix='.gir', delete=False)
350 passthrough_gir(main_f.name, temp_f)
352 if not files_are_identical(main_f.name, temp_f.name):
353 _error("Failed to re-parse gir file; scanned=%r passthrough=%r" % (
354 main_f.name, temp_f.name))
355 os.unlink(temp_f.name)
357 shutil.move(main_f.name, options.output)
359 if e.errno == errno.EPERM:
360 os.unlink(main_f.name)
366 output = open(options.output, "w")
368 _error("opening output for writing: %s" % (e.strerror, ))
373 _error("while writing output: %s" % (e.strerror, ))