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.maintransformer import MainTransformer
37 from giscanner.introspectablepass import IntrospectablePass
38 from giscanner.girparser import GIRParser
39 from giscanner.girwriter import GIRWriter
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(parser, 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 pkg_options, unused = parser.parse_args(filtered_output)
200 options.cpp_includes.extend(pkg_options.cpp_includes)
201 options.cpp_defines.extend(pkg_options.cpp_defines)
202 options.cpp_undefines.extend(pkg_options.cpp_undefines)
204 args = ['pkg-config', '--libs-only-L']
205 args.extend(packages)
206 output = subprocess.Popen(args,
207 stdout=subprocess.PIPE).communicate()[0]
210 filtered_output = list(process_options(output, options_whitelist))
211 pkg_options, unused = parser.parse_args(filtered_output)
212 options.library_paths.extend(pkg_options.library_paths)
214 def scanner_main(args):
215 parser = _get_option_parser()
216 (options, args) = parser.parse_args(args)
218 if options.passthrough_gir:
219 passthrough_gir(options.passthrough_gir, sys.stdout)
220 if options.test_codegen:
221 return test_codegen(options.test_codegen)
224 _error('Need at least one filename')
226 if not options.namespace_name:
227 _error('Namespace name missing')
229 if options.format == 'gir':
230 from giscanner.girwriter import GIRWriter as Writer
232 _error("Unknown format: %s" % (options.format, ))
234 if not (options.libraries or options.program):
235 _error("Must specify --program or --library")
236 libraries = options.libraries
240 # We don't support real C++ parsing yet, but we should be able
241 # to understand C API implemented in C++ files.
242 if (arg.endswith('.c') or arg.endswith('.cpp') or
243 arg.endswith('.cc') or arg.endswith('.cxx') or
244 arg.endswith('.h') or arg.endswith('.hpp') or
245 arg.endswith('.hxx')):
246 if not os.path.exists(arg):
247 _error('%s: no such a file or directory' % (arg, ))
248 # Make absolute, because we do comparisons inside scannerparser.c
249 # against the absolute path that cpp will give us
250 filenames.append(os.path.abspath(arg))
252 # We do this dance because the empty list has different semantics from
253 # None; if the user didn't specify the options, we want to use None so
254 # the Namespace constructor picks the defaults.
255 if options.identifier_prefixes:
256 identifier_prefixes = options.identifier_prefixes
258 identifier_prefixes = None
259 if options.symbol_prefixes:
260 symbol_prefixes = options.symbol_prefixes
262 symbol_prefixes = None
264 namespace = Namespace(options.namespace_name,
265 options.namespace_version,
266 identifier_prefixes=identifier_prefixes,
267 symbol_prefixes=symbol_prefixes)
268 message.MessageLogger.get(namespace=namespace,
269 enable_warnings=options.warn_all)
270 transformer = Transformer(namespace,
271 accept_unprefixed=options.accept_unprefixed)
272 transformer.set_include_paths(options.include_paths)
273 shown_include_warning = False
274 for include in options.includes:
275 if os.sep in include:
276 _error("Invalid include path %r" % (include, ))
278 include_obj = Include.from_string(include)
280 _error("Malformed include %r\n" % (include, ))
281 transformer.register_include(include_obj)
283 packages = set(options.packages)
284 packages.update(transformer.get_pkgconfig_packages())
285 exit_code = process_packages(parser, options, packages)
289 # Run the preprocessor, tokenize and construct simple
290 # objects representing the raw C symbols
292 ss.set_cpp_options(options.cpp_includes,
294 options.cpp_undefines)
295 ss.parse_files(filenames)
296 ss.parse_macros(filenames)
298 # Transform the C symbols into AST nodes
299 transformer.parse(ss.get_symbols())
301 # Transform the C AST nodes into higher level
303 gdump_parser = GDumpParser(transformer)
305 # Do enough parsing that we have the get_type() functions to reference
306 # when creating the introspection binary
307 gdump_parser.init_parse()
310 args=[options.program]
311 args.extend(options.program_args)
312 binary = IntrospectionBinary(args)
314 binary = compile_introspection_binary(options,
315 gdump_parser.get_get_type_functions())
317 shlibs = resolve_shlibs(options, binary, libraries)
319 gdump_parser.set_introspection_binary(binary)
322 ap = AnnotationParser()
323 blocks = ap.parse(ss.get_comments())
325 main = MainTransformer(transformer, blocks)
328 final = IntrospectablePass(transformer)
331 if options.warn_fatal and transformer.did_warn():
332 transformer.log_warning("warnings configured as fatal", fatal=True)
336 if options.packages_export:
337 exported_packages = options.packages_export
339 exported_packages = options.packages
340 writer = Writer(transformer.namespace, shlibs, transformer.get_includes(),
341 exported_packages, options.c_includes)
342 data = writer.get_xml()
344 if options.output == "-":
346 elif options.reparse_validate_gir:
347 main_f = tempfile.NamedTemporaryFile(suffix='.gir', delete=False)
351 temp_f = tempfile.NamedTemporaryFile(suffix='.gir', delete=False)
352 passthrough_gir(main_f.name, temp_f)
354 if not files_are_identical(main_f.name, temp_f.name):
355 _error("Failed to re-parse gir file; scanned=%r passthrough=%r" % (
356 main_f.name, temp_f.name))
357 os.unlink(temp_f.name)
359 shutil.move(main_f.name, options.output)
361 if e.errno == errno.EPERM:
362 os.unlink(main_f.name)
368 output = open(options.output, "w")
370 _error("opening output for writing: %s" % (e.strerror, ))
375 _error("while writing output: %s" % (e.strerror, ))