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.cachestore import CacheStore
34 from giscanner.dumper import compile_introspection_binary
35 from giscanner.gdumpparser import GDumpParser, IntrospectionBinary
36 from giscanner.maintransformer import MainTransformer
37 from giscanner.minixpath import xpath_assert
38 from giscanner.introspectablepass import IntrospectablePass
39 from giscanner.girparser import GIRParser
40 from giscanner.girwriter import GIRWriter
41 from giscanner.shlibs import resolve_shlibs
42 from giscanner.sourcescanner import SourceScanner
43 from giscanner.transformer import Transformer
44 from giscanner.utils import files_are_identical
46 def _get_option_parser():
47 parser = optparse.OptionParser('%prog [options] sources')
48 parser.add_option('', "--quiet",
49 action="store_true", dest="quiet",
51 help="If passed, do not print details of normal" \
53 parser.add_option("", "--format",
54 action="store", dest="format",
56 help="format to use, one of gidl, gir")
57 parser.add_option("-i", "--include",
58 action="append", dest="includes", default=[],
59 help="include types for other gidls")
60 parser.add_option('', "--generate-typelib-tests",
61 action="store", dest="test_codegen", default=None,
62 help="Generate test code for given namespace,output.h,output.c")
63 parser.add_option('', "--passthrough-gir",
64 action="store", dest="passthrough_gir", default=None,
65 help="Parse and re-output the specified GIR")
66 parser.add_option('', "--reparse-validate",
67 action="store_true", dest="reparse_validate_gir", default=False,
68 help="After generating the GIR, re-parse it to ensure validity")
69 parser.add_option("", "--add-include-path",
70 action="append", dest="include_paths", default=[],
71 help="include paths for other GIR files")
72 parser.add_option("", "--program",
73 action="store", dest="program", default=None,
74 help="program to execute")
75 parser.add_option("", "--program-arg",
76 action="append", dest="program_args", default=[],
77 help="extra arguments to program")
78 parser.add_option("", "--libtool",
79 action="store", dest="libtool_path", default=None,
80 help="full path to libtool")
81 parser.add_option("", "--no-libtool",
82 action="store_true", dest="nolibtool", default=False,
83 help="do not use libtool")
84 parser.add_option("-l", "--library",
85 action="append", dest="libraries", default=[],
86 help="libraries of this unit")
87 parser.add_option("-L", "--library-path",
88 action="append", dest="library_paths", default=[],
89 help="directories to search for libraries")
90 parser.add_option("-n", "--namespace",
91 action="store", dest="namespace_name",
92 help=("name of namespace for this unit, also "
93 "used to compute --identifier-prefix and --symbol-prefix"))
94 parser.add_option("", "--nsversion",
95 action="store", dest="namespace_version",
96 help="version of namespace for this unit")
97 parser.add_option("", "--identifier-prefix",
98 action="append", dest="identifier_prefixes", default=[],
99 help="""Remove this prefix from C identifiers (structure typedefs, etc.).
100 May be specified multiple times. This is also used as the default for --symbol-prefix if
101 the latter is not specified.""")
102 parser.add_option("", "--symbol-prefix",
103 action="append", dest="symbol_prefixes", default=[],
104 help="Remove this prefix from C symbols (function names)")
105 parser.add_option("", "--accept-unprefixed",
106 action="store_true", dest="accept_unprefixed", default=False,
107 help="""If specified, accept symbols and identifiers that do not
108 match the namespace prefix.""")
109 parser.add_option("", "--add-init-section",
110 action="append", dest="init_sections", default=[],
111 help="add extra initialization code in the introspection program")
112 parser.add_option("-o", "--output",
113 action="store", dest="output", default="-",
114 help="output filename to write to, defaults to - (stdout)")
115 parser.add_option("", "--pkg",
116 action="append", dest="packages", default=[],
117 help="pkg-config packages to get cflags from")
118 parser.add_option("", "--pkg-export",
119 action="append", dest="packages_export", default=[],
120 help="Associated pkg-config packages for this library")
121 parser.add_option('', "--warn-all",
122 action="store_true", dest="warn_all", default=False,
123 help="If true, enable all warnings for introspection")
124 parser.add_option('', "--warn-error",
125 action="store_true", dest="warn_fatal",
126 help="Turn warnings into fatal errors")
127 parser.add_option("-v", "--verbose",
128 action="store_true", dest="verbose",
130 parser.add_option("", "--typelib-xml",
131 action="store_true", dest="typelib_xml",
132 help="Just convert GIR to typelib XML")
133 parser.add_option("", "--xpath-assertions",
134 action="store", dest="xpath_assertions",
135 help="Use given file to create assertions on GIR content")
136 parser.add_option("", "--c-include",
137 action="append", dest="c_includes", default=[],
138 help="headers which should be included in C programs")
140 group = optparse.OptionGroup(parser, "Preprocessor options")
141 group.add_option("-I", help="Pre-processor include file",
142 action="append", dest="cpp_includes",
144 group.add_option("-D", help="Pre-processor define",
145 action="append", dest="cpp_defines",
147 group.add_option("-U", help="Pre-processor undefine",
148 action="append", dest="cpp_undefines",
150 group.add_option("-p", dest="", help="Ignored")
151 parser.add_option_group(group)
157 raise SystemExit('ERROR: %s' % (msg, ))
159 def passthrough_gir(path, f):
163 writer = GIRWriter(parser.get_namespace(),
164 parser.get_shared_libraries(),
165 parser.get_includes(),
166 parser.get_pkgconfig_packages(),
167 parser.get_c_includes())
168 f.write(writer.get_xml())
170 def test_codegen(optstring):
171 (namespace, out_h_filename, out_c_filename) = optstring.split(',')
172 if namespace == 'Everything':
173 from .testcodegen import EverythingCodeGenerator
174 gen = EverythingCodeGenerator(out_h_filename, out_c_filename)
177 raise ValueError("Invaild namespace %r" % (namespace, ))
180 def validate(assertions, path):
181 from xml.etree.cElementTree import parse
182 doc = parse(open(path))
185 assertions_list = f.readlines()
186 for assertion in assertions_list:
187 assertion = assertion.strip()
188 xpath_assert(root, assertion)
192 def process_options(output, allowed_flags):
193 for option in output.split():
194 for flag in allowed_flags:
195 if not option.startswith(flag):
200 def process_packages(parser, options, packages):
201 args = ['pkg-config', '--cflags']
202 args.extend(packages)
203 output = subprocess.Popen(args,
204 stdout=subprocess.PIPE).communicate()[0]
206 # the error output should have already appeared on our stderr,
209 # Some pkg-config files on Windows have options we don't understand,
210 # so we explicitly filter to only the ones we need.
211 options_whitelist = ['-I', '-D', '-U', '-l', '-L']
212 filtered_output = list(process_options(output, options_whitelist))
213 pkg_options, unused = parser.parse_args(filtered_output)
214 options.cpp_includes.extend(pkg_options.cpp_includes)
215 options.cpp_defines.extend(pkg_options.cpp_defines)
216 options.cpp_undefines.extend(pkg_options.cpp_undefines)
218 args = ['pkg-config', '--libs-only-L']
219 args.extend(packages)
220 output = subprocess.Popen(args,
221 stdout=subprocess.PIPE).communicate()[0]
224 filtered_output = list(process_options(output, options_whitelist))
225 pkg_options, unused = parser.parse_args(filtered_output)
226 options.library_paths.extend(pkg_options.library_paths)
228 def scanner_main(args):
229 parser = _get_option_parser()
230 (options, args) = parser.parse_args(args)
232 if options.passthrough_gir:
233 passthrough_gir(options.passthrough_gir, sys.stdout)
234 if options.test_codegen:
235 return test_codegen(options.test_codegen)
238 _error('Need at least one filename')
240 if options.xpath_assertions:
241 return validate(options.xpath_assertions, args[1])
243 if not options.namespace_name:
244 _error('Namespace name missing')
246 if options.format == 'gir':
247 from giscanner.girwriter import GIRWriter as Writer
249 _error("Unknown format: %s" % (options.format, ))
251 if not (options.libraries or options.program):
252 _error("Must specify --program or --library")
253 libraries = options.libraries
257 # We don't support real C++ parsing yet, but we should be able
258 # to understand C API implemented in C++ files.
259 if (arg.endswith('.c') or arg.endswith('.cpp') or
260 arg.endswith('.cc') or arg.endswith('.cxx') or
261 arg.endswith('.h') or arg.endswith('.hpp') or
262 arg.endswith('.hxx')):
263 if not os.path.exists(arg):
264 _error('%s: no such a file or directory' % (arg, ))
265 # Make absolute, because we do comparisons inside scannerparser.c
266 # against the absolute path that cpp will give us
267 filenames.append(os.path.abspath(arg))
269 # We do this dance because the empty list has different semantics from
270 # None; if the user didn't specify the options, we want to use None so
271 # the Namespace constructor picks the defaults.
272 if options.identifier_prefixes:
273 identifier_prefixes = options.identifier_prefixes
275 identifier_prefixes = None
276 if options.symbol_prefixes:
277 symbol_prefixes = options.symbol_prefixes
279 symbol_prefixes = None
281 cachestore = CacheStore()
282 transformer = Transformer(cachestore,
283 options.namespace_name,
284 options.namespace_version,
285 identifier_prefixes=identifier_prefixes,
286 symbol_prefixes=symbol_prefixes,
287 accept_unprefixed=options.accept_unprefixed)
289 transformer.enable_warnings(True)
290 transformer.set_include_paths(options.include_paths)
291 shown_include_warning = False
292 for include in options.includes:
293 if os.sep in include:
294 raise ValueError("Invalid include path %r" % (include, ))
296 include_obj = Include.from_string(include)
298 sys.stderr.write("Malformed include %r\n" % (include, ))
300 transformer.register_include(include_obj)
302 packages = set(options.packages)
303 packages.update(transformer.get_pkgconfig_packages())
304 exit_code = process_packages(parser, options, packages)
308 # Run the preprocessor, tokenize and construct simple
309 # objects representing the raw C symbols
311 ss.set_cpp_options(options.cpp_includes,
313 options.cpp_undefines)
314 ss.parse_files(filenames)
315 ss.parse_macros(filenames)
317 # Transform the C symbols into AST nodes
318 transformer.set_source_ast(ss)
320 # Transform the C AST nodes into higher level
322 gdump_parser = GDumpParser(transformer)
324 # Do enough parsing that we have the get_type() functions to reference
325 # when creating the introspection binary
326 gdump_parser.init_parse()
329 args=[options.program]
330 args.extend(options.program_args)
331 binary = IntrospectionBinary(args)
333 binary = compile_introspection_binary(options,
334 gdump_parser.get_get_type_functions())
336 shlibs = resolve_shlibs(options, binary, libraries)
338 gdump_parser.set_introspection_binary(binary)
341 ap = AnnotationParser(ss)
344 main = MainTransformer(transformer, blocks)
347 final = IntrospectablePass(transformer)
350 if options.warn_fatal and transformer.did_warn():
351 transformer.log_warning("warnings configured as fatal", fatal=True)
355 if options.packages_export:
356 exported_packages = options.packages_export
358 exported_packages = options.packages
359 writer = Writer(transformer.namespace, shlibs, transformer.get_includes(),
360 exported_packages, options.c_includes)
361 data = writer.get_xml()
363 if options.output == "-":
365 elif options.reparse_validate_gir:
366 main_f = tempfile.NamedTemporaryFile(suffix='.gir', delete=False)
370 temp_f = tempfile.NamedTemporaryFile(suffix='.gir', delete=False)
371 passthrough_gir(main_f.name, temp_f)
373 if not files_are_identical(main_f.name, temp_f.name):
375 "Failed to re-parse .gir file; scanned=%r passthrough=%r" % (main_f.name, temp_f.name))
376 os.unlink(temp_f.name)
378 shutil.move(main_f.name, options.output)
380 if e.errno == errno.EPERM:
381 os.unlink(main_f.name)
387 output = open(options.output, "w")
389 _error("opening output for writing: %s" % (e.strerror, ))
394 _error("while writing output: %s" % (e.strerror, ))