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, Namespace
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 namespace = Namespace(options.namespace_name,
264 options.namespace_version,
265 identifier_prefixes=identifier_prefixes,
266 symbol_prefixes=symbol_prefixes)
267 transformer = Transformer(namespace,
268 accept_unprefixed=options.accept_unprefixed)
270 transformer.enable_warnings(True)
271 transformer.set_include_paths(options.include_paths)
272 shown_include_warning = False
273 for include in options.includes:
274 if os.sep in include:
275 _error("Invalid include path %r" % (include, ))
277 include_obj = Include.from_string(include)
279 _error("Malformed include %r\n" % (include, ))
280 transformer.register_include(include_obj)
282 packages = set(options.packages)
283 packages.update(transformer.get_pkgconfig_packages())
284 exit_code = process_packages(parser, options, packages)
288 # Run the preprocessor, tokenize and construct simple
289 # objects representing the raw C symbols
291 ss.set_cpp_options(options.cpp_includes,
293 options.cpp_undefines)
294 ss.parse_files(filenames)
295 ss.parse_macros(filenames)
297 # Transform the C symbols into AST nodes
298 transformer.parse(ss.get_symbols())
300 # Transform the C AST nodes into higher level
302 gdump_parser = GDumpParser(transformer)
304 # Do enough parsing that we have the get_type() functions to reference
305 # when creating the introspection binary
306 gdump_parser.init_parse()
309 args=[options.program]
310 args.extend(options.program_args)
311 binary = IntrospectionBinary(args)
313 binary = compile_introspection_binary(options,
314 gdump_parser.get_get_type_functions())
316 shlibs = resolve_shlibs(options, binary, libraries)
318 gdump_parser.set_introspection_binary(binary)
321 ap = AnnotationParser()
322 blocks = ap.parse(ss.get_comments())
324 main = MainTransformer(transformer, blocks)
327 final = IntrospectablePass(transformer)
330 if options.warn_fatal and transformer.did_warn():
331 transformer.log_warning("warnings configured as fatal", fatal=True)
335 if options.packages_export:
336 exported_packages = options.packages_export
338 exported_packages = options.packages
339 writer = Writer(transformer.namespace, shlibs, transformer.get_includes(),
340 exported_packages, options.c_includes)
341 data = writer.get_xml()
343 if options.output == "-":
345 elif options.reparse_validate_gir:
346 main_f = tempfile.NamedTemporaryFile(suffix='.gir', delete=False)
350 temp_f = tempfile.NamedTemporaryFile(suffix='.gir', delete=False)
351 passthrough_gir(main_f.name, temp_f)
353 if not files_are_identical(main_f.name, temp_f.name):
354 _error("Failed to re-parse gir file; scanned=%r passthrough=%r" % (
355 main_f.name, temp_f.name))
356 os.unlink(temp_f.name)
358 shutil.move(main_f.name, options.output)
360 if e.errno == errno.EPERM:
361 os.unlink(main_f.name)
367 output = open(options.output, "w")
369 _error("opening output for writing: %s" % (e.strerror, ))
374 _error("while writing output: %s" % (e.strerror, ))