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="include types for other gidls")
59 parser.add_option("", "--add-include-path",
60 action="append", dest="include_paths", default=[],
61 help="include paths for other GIR files")
62 parser.add_option("", "--program",
63 action="store", dest="program", default=None,
64 help="program to execute")
65 parser.add_option("", "--program-arg",
66 action="append", dest="program_args", default=[],
67 help="extra arguments to program")
68 parser.add_option("", "--libtool",
69 action="store", dest="libtool_path", default=None,
70 help="full path to libtool")
71 parser.add_option("", "--no-libtool",
72 action="store_true", dest="nolibtool", default=False,
73 help="do not use libtool")
74 parser.add_option("-l", "--library",
75 action="append", dest="libraries", default=[],
76 help="libraries of this unit")
77 parser.add_option("-L", "--library-path",
78 action="append", dest="library_paths", default=[],
79 help="directories to search for libraries")
80 parser.add_option("-n", "--namespace",
81 action="store", dest="namespace_name",
82 help=("name of namespace for this unit, also "
83 "used to compute --identifier-prefix and --symbol-prefix"))
84 parser.add_option("", "--nsversion",
85 action="store", dest="namespace_version",
86 help="version of namespace for this unit")
87 parser.add_option("", "--identifier-prefix",
88 action="append", dest="identifier_prefixes", default=[],
89 help="""Remove this prefix from C identifiers (structure typedefs, etc.).
90 May be specified multiple times. This is also used as the default for --symbol-prefix if
91 the latter is not specified.""")
92 parser.add_option("", "--symbol-prefix",
93 action="append", dest="symbol_prefixes", default=[],
94 help="Remove this prefix from C symbols (function names)")
95 parser.add_option("", "--accept-unprefixed",
96 action="store_true", dest="accept_unprefixed", default=False,
97 help="""If specified, accept symbols and identifiers that do not
98 match the namespace prefix.""")
99 parser.add_option("", "--add-init-section",
100 action="append", dest="init_sections", default=[],
101 help="add extra initialization code in the introspection program")
102 parser.add_option("-o", "--output",
103 action="store", dest="output", default="-",
104 help="output filename to write to, defaults to - (stdout)")
105 parser.add_option("", "--pkg",
106 action="append", dest="packages", default=[],
107 help="pkg-config packages to get cflags from")
108 parser.add_option("", "--pkg-export",
109 action="append", dest="packages_export", default=[],
110 help="Associated pkg-config packages for this library")
111 parser.add_option('', "--warn-all",
112 action="store_true", dest="warn_all", default=False,
113 help="If true, enable all warnings for introspection")
114 parser.add_option('', "--warn-error",
115 action="store_true", dest="warn_fatal",
116 help="Turn warnings into fatal errors")
117 parser.add_option("-v", "--verbose",
118 action="store_true", dest="verbose",
120 parser.add_option("", "--c-include",
121 action="append", dest="c_includes", default=[],
122 help="headers which should be included in C programs")
124 group = optparse.OptionGroup(parser, "Preprocessor options")
125 group.add_option("-I", help="Pre-processor include file",
126 action="append", dest="cpp_includes",
128 group.add_option("-D", help="Pre-processor define",
129 action="append", dest="cpp_defines",
131 group.add_option("-U", help="Pre-processor undefine",
132 action="append", dest="cpp_undefines",
134 group.add_option("-p", dest="", help="Ignored")
135 parser.add_option_group(group)
138 parser.add_option('', "--generate-typelib-tests",
139 action="store", dest="test_codegen", default=None,
140 help=optparse.SUPPRESS_HELP)
141 parser.add_option('', "--passthrough-gir",
142 action="store", dest="passthrough_gir", default=None,
143 help=optparse.SUPPRESS_HELP)
144 parser.add_option('', "--reparse-validate",
145 action="store_true", dest="reparse_validate_gir", default=False,
146 help=optparse.SUPPRESS_HELP)
147 parser.add_option("", "--typelib-xml",
148 action="store_true", dest="typelib_xml",
149 help=optparse.SUPPRESS_HELP)
155 raise SystemExit('ERROR: %s' % (msg, ))
157 def passthrough_gir(path, f):
161 writer = GIRWriter(parser.get_namespace(),
162 parser.get_shared_libraries(),
163 parser.get_includes(),
164 parser.get_pkgconfig_packages(),
165 parser.get_c_includes())
166 f.write(writer.get_xml())
168 def test_codegen(optstring):
169 (namespace, out_h_filename, out_c_filename) = optstring.split(',')
170 if namespace == 'Everything':
171 from .testcodegen import EverythingCodeGenerator
172 gen = EverythingCodeGenerator(out_h_filename, out_c_filename)
175 _error("Invaild namespace %r" % (namespace, ))
178 def process_options(output, allowed_flags):
179 for option in output.split():
180 for flag in allowed_flags:
181 if not option.startswith(flag):
186 def process_packages(options, packages):
187 args = ['pkg-config', '--cflags']
188 args.extend(packages)
189 output = subprocess.Popen(args,
190 stdout=subprocess.PIPE).communicate()[0]
192 # the error output should have already appeared on our stderr,
195 # Some pkg-config files on Windows have options we don't understand,
196 # so we explicitly filter to only the ones we need.
197 options_whitelist = ['-I', '-D', '-U', '-l', '-L']
198 filtered_output = list(process_options(output, options_whitelist))
199 parser = _get_option_parser()
200 pkg_options, unused = parser.parse_args(filtered_output)
201 options.cpp_includes.extend(pkg_options.cpp_includes)
202 options.cpp_defines.extend(pkg_options.cpp_defines)
203 options.cpp_undefines.extend(pkg_options.cpp_undefines)
205 args = ['pkg-config', '--libs-only-L']
206 args.extend(packages)
207 output = subprocess.Popen(args,
208 stdout=subprocess.PIPE).communicate()[0]
211 filtered_output = list(process_options(output, options_whitelist))
212 pkg_options, unused = parser.parse_args(filtered_output)
213 options.library_paths.extend(pkg_options.library_paths)
215 def scanner_main(args):
216 parser = _get_option_parser()
217 (options, args) = parser.parse_args(args)
219 if options.passthrough_gir:
220 passthrough_gir(options.passthrough_gir, sys.stdout)
221 if options.test_codegen:
222 return test_codegen(options.test_codegen)
225 _error('Need at least one filename')
227 if not options.namespace_name:
228 _error('Namespace name missing')
230 if options.format == 'gir':
231 from giscanner.girwriter import GIRWriter as Writer
233 _error("Unknown format: %s" % (options.format, ))
235 if not (options.libraries or options.program):
236 _error("Must specify --program or --library")
237 libraries = options.libraries
241 # We don't support real C++ parsing yet, but we should be able
242 # to understand C API implemented in C++ files.
243 if (arg.endswith('.c') or arg.endswith('.cpp') or
244 arg.endswith('.cc') or arg.endswith('.cxx') or
245 arg.endswith('.h') or arg.endswith('.hpp') or
246 arg.endswith('.hxx')):
247 if not os.path.exists(arg):
248 _error('%s: no such a file or directory' % (arg, ))
249 # Make absolute, because we do comparisons inside scannerparser.c
250 # against the absolute path that cpp will give us
251 filenames.append(os.path.abspath(arg))
253 # We do this dance because the empty list has different semantics from
254 # None; if the user didn't specify the options, we want to use None so
255 # the Namespace constructor picks the defaults.
256 if options.identifier_prefixes:
257 identifier_prefixes = options.identifier_prefixes
259 identifier_prefixes = None
260 if options.symbol_prefixes:
261 symbol_prefixes = options.symbol_prefixes
263 symbol_prefixes = None
265 namespace = Namespace(options.namespace_name,
266 options.namespace_version,
267 identifier_prefixes=identifier_prefixes,
268 symbol_prefixes=symbol_prefixes)
269 logger = message.MessageLogger.get(namespace=namespace)
271 logger.enable_warnings(True)
272 transformer = Transformer(namespace,
273 accept_unprefixed=options.accept_unprefixed)
274 transformer.set_include_paths(options.include_paths)
275 shown_include_warning = False
276 for include in options.includes:
277 if os.sep in include:
278 _error("Invalid include path %r" % (include, ))
280 include_obj = Include.from_string(include)
282 _error("Malformed include %r\n" % (include, ))
283 transformer.register_include(include_obj)
285 packages = set(options.packages)
286 packages.update(transformer.get_pkgconfig_packages())
287 exit_code = process_packages(options, packages)
291 # Run the preprocessor, tokenize and construct simple
292 # objects representing the raw C symbols
294 ss.set_cpp_options(options.cpp_includes,
296 options.cpp_undefines)
297 ss.parse_files(filenames)
298 ss.parse_macros(filenames)
300 # Transform the C symbols into AST nodes
301 transformer.parse(ss.get_symbols())
303 # Transform the C AST nodes into higher level
305 gdump_parser = GDumpParser(transformer)
307 # Do enough parsing that we have the get_type() functions to reference
308 # when creating the introspection binary
309 gdump_parser.init_parse()
312 args=[options.program]
313 args.extend(options.program_args)
314 binary = IntrospectionBinary(args)
316 binary = compile_introspection_binary(options,
317 gdump_parser.get_get_type_functions())
319 shlibs = resolve_shlibs(options, binary, libraries)
321 gdump_parser.set_introspection_binary(binary)
324 ap = AnnotationParser()
325 blocks = ap.parse(ss.get_comments())
327 main = MainTransformer(transformer, blocks)
330 final = IntrospectablePass(transformer)
333 if options.warn_fatal and logger.did_warn():
334 message.fatal("warnings configured as fatal")
338 if options.packages_export:
339 exported_packages = options.packages_export
341 exported_packages = options.packages
342 writer = Writer(transformer.namespace, shlibs, transformer.get_includes(),
343 exported_packages, options.c_includes)
344 data = writer.get_xml()
346 if options.output == "-":
348 elif options.reparse_validate_gir:
349 main_f = tempfile.NamedTemporaryFile(suffix='.gir', delete=False)
353 temp_f = tempfile.NamedTemporaryFile(suffix='.gir', delete=False)
354 passthrough_gir(main_f.name, temp_f)
356 if not files_are_identical(main_f.name, temp_f.name):
357 _error("Failed to re-parse gir file; scanned=%r passthrough=%r" % (
358 main_f.name, temp_f.name))
359 os.unlink(temp_f.name)
361 shutil.move(main_f.name, options.output)
363 if e.errno == errno.EPERM:
364 os.unlink(main_f.name)
370 output = open(options.output, "w")
372 _error("opening output for writing: %s" % (e.strerror, ))
377 _error("while writing output: %s" % (e.strerror, ))